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 /jdisc_core/src/test |
Publish
Diffstat (limited to 'jdisc_core/src/test')
106 files changed, 13917 insertions, 0 deletions
diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/AbstractResourceTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/AbstractResourceTestCase.java new file mode 100644 index 00000000000..d43771c9cfd --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/AbstractResourceTestCase.java @@ -0,0 +1,133 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class AbstractResourceTestCase { + + @Test + public void requireThatDestroyIsCalledWhenReleased() { + MyResource res = new MyResource(); + assertFalse(res.destroyed); + res.release(); + assertTrue(res.destroyed); + } + + @Test + public void requireThatDestroyIsCalledWhenRetainCountReachesZero() { + MyResource res = new MyResource(); + assertEquals(1, res.retainCount()); + assertFalse(res.destroyed); + final ResourceReference reference = res.refer(); + assertEquals(2, res.retainCount()); + res.release(); + assertEquals(1, res.retainCount()); + assertFalse(res.destroyed); + reference.close(); + assertEquals(0, res.retainCount()); + assertTrue(res.destroyed); + } + + @Test + public void requireThatDestroyIsCalledWhenRetainCountReachesZeroOppositeOrder() { + MyResource res = new MyResource(); + assertEquals(1, res.retainCount()); + assertFalse(res.destroyed); + final ResourceReference reference = res.refer(); + assertEquals(2, res.retainCount()); + reference.close(); + assertEquals(1, res.retainCount()); + assertFalse(res.destroyed); + res.release(); + assertEquals(0, res.retainCount()); + assertTrue(res.destroyed); + } + + @Test + public void requireThatReleaseCanOnlyBeCalledOnceEvenWhenReferenceCountIsPositive() { + MyResource res = new MyResource(); + final ResourceReference secondReference = res.refer(); + res.release(); + try { + res.release(); + fail(); + } catch (IllegalStateException e) { + // As expected. + } + secondReference.close(); + } + + @Test + public void requireThatSecondaryReferenceCanOnlyBeClosedOnceEvenWhenReferenceCountIsPositive() { + MyResource res = new MyResource(); + final ResourceReference secondReference = res.refer(); + secondReference.close(); + try { + secondReference.close(); + fail(); + } catch (IllegalStateException e) { + // As expected. + } + res.release(); + } + + @Test + public void requireThatReleaseAfterDestroyThrows() { + MyResource res = new MyResource(); + res.release(); + assertTrue(res.destroyed); + try { + res.release(); + fail(); + } catch (IllegalStateException e) { + + } + assertEquals(0, res.retainCount()); + try { + res.release(); + fail(); + } catch (IllegalStateException e) { + + } + assertEquals(0, res.retainCount()); + } + + @Test + public void requireThatReferAfterDestroyThrows() { + MyResource res = new MyResource(); + res.release(); + assertTrue(res.destroyed); + try { + res.refer(); + fail(); + } catch (IllegalStateException e) { + + } + assertEquals(0, res.retainCount()); + try { + res.refer(); + fail(); + } catch (IllegalStateException e) { + + } + assertEquals(0, res.retainCount()); + } + + private static class MyResource extends AbstractResource { + + boolean destroyed = false; + + @Override + protected void destroy() { + destroyed = true; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/ContainerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/ContainerTestCase.java new file mode 100644 index 00000000000..9eef00b2cbe --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/ContainerTestCase.java @@ -0,0 +1,49 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc; + +import com.google.inject.AbstractModule; +import com.yahoo.jdisc.service.BindingSetNotFoundException; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ContainerTestCase { + + @Test + public void requireThatNewRequestsReferenceSameSnapshot() throws Exception { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + driver.activateContainer(driver.newContainerBuilder()); + Request foo = new Request(driver, URI.create("http://foo")); + Request bar = new Request(foo, URI.create("http://bar")); + assertNotNull(foo.container()); + assertSame(foo.container(), bar.container()); + foo.release(); + bar.release(); + driver.close(); + } + + @Test + public void requireThatInjectionWorks() throws BindingSetNotFoundException { + final Object foo = new Object(); + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(new AbstractModule() { + + @Override + protected void configure() { + bind(Object.class).toInstance(foo); + } + }); + driver.activateContainer(driver.newContainerBuilder()); + Request request = new Request(driver, URI.create("http://host/path")); + assertSame(foo, request.container().getInstance(Object.class)); + request.release(); + driver.close(); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/HeaderFieldsTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/HeaderFieldsTestCase.java new file mode 100644 index 00000000000..2d0ada8113c --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/HeaderFieldsTestCase.java @@ -0,0 +1,372 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class HeaderFieldsTestCase { + + @Test + public void requireThatSizeWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertEquals(0, headers.size()); + headers.add("foo", "bar"); + assertEquals(1, headers.size()); + headers.add("foo", "baz"); + assertEquals(1, headers.size()); + headers.add("bar", "baz"); + assertEquals(2, headers.size()); + headers.remove("foo"); + assertEquals(1, headers.size()); + headers.remove("bar"); + assertEquals(0, headers.size()); + } + + @Test + public void requireThatIsEmptyWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertTrue(headers.isEmpty()); + headers.add("foo", "bar"); + assertFalse(headers.isEmpty()); + headers.remove("foo"); + assertTrue(headers.isEmpty()); + } + + @Test + public void requireThatContainsKeyWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertFalse(headers.containsKey("foo")); + assertFalse(headers.containsKey("FOO")); + headers.add("foo", "bar"); + assertTrue(headers.containsKey("foo")); + assertTrue(headers.containsKey("FOO")); + } + + @Test + public void requireThatContainsValueWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertFalse(headers.containsValue(Arrays.asList("bar"))); + headers.add("foo", "bar"); + assertTrue(headers.containsValue(Arrays.asList("bar"))); + } + + @Test + public void requireThatContainsWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertFalse(headers.contains("foo", "bar")); + assertFalse(headers.contains("FOO", "bar")); + assertFalse(headers.contains("foo", "BAR")); + assertFalse(headers.contains("FOO", "BAR")); + headers.add("foo", "bar"); + assertTrue(headers.contains("foo", "bar")); + assertTrue(headers.contains("FOO", "bar")); + assertFalse(headers.contains("foo", "BAR")); + assertFalse(headers.contains("FOO", "BAR")); + } + + @Test + public void requireThatContainsIgnoreCaseWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertFalse(headers.containsIgnoreCase("foo", "bar")); + assertFalse(headers.containsIgnoreCase("FOO", "bar")); + assertFalse(headers.containsIgnoreCase("foo", "BAR")); + assertFalse(headers.containsIgnoreCase("FOO", "BAR")); + headers.add("foo", "bar"); + assertTrue(headers.containsIgnoreCase("foo", "bar")); + assertTrue(headers.containsIgnoreCase("FOO", "bar")); + assertTrue(headers.containsIgnoreCase("foo", "BAR")); + assertTrue(headers.containsIgnoreCase("FOO", "BAR")); + } + + @Test + public void requireThatAddStringWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertNull(headers.get("foo")); + headers.add("foo", "bar"); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + headers.add("foo", "baz"); + assertEquals(Arrays.asList("bar", "baz"), headers.get("foo")); + } + + @Test + public void requireThatAddListWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertNull(headers.get("foo")); + headers.add("foo", Arrays.asList("bar")); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + headers.add("foo", Arrays.asList("baz", "cox")); + assertEquals(Arrays.asList("bar", "baz", "cox"), headers.get("foo")); + } + + @Test + public void requireThatAddAllWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + headers.add("foo", "bar"); + headers.add("bar", "baz"); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + assertEquals(Arrays.asList("baz"), headers.get("bar")); + + Map<String, List<String>> map = new HashMap<>(); + map.put("foo", Arrays.asList("baz", "cox")); + map.put("bar", Arrays.asList("cox")); + headers.addAll(map); + + assertEquals(Arrays.asList("bar", "baz", "cox"), headers.get("foo")); + assertEquals(Arrays.asList("baz", "cox"), headers.get("bar")); + } + + @Test + public void requireThatPutStringWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertNull(headers.get("foo")); + headers.put("foo", "bar"); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + headers.put("foo", "baz"); + assertEquals(Arrays.asList("baz"), headers.get("foo")); + } + + @Test + public void requireThatPutListWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertNull(headers.get("foo")); + headers.put("foo", Arrays.asList("bar")); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + headers.put("foo", Arrays.asList("baz", "cox")); + assertEquals(Arrays.asList("baz", "cox"), headers.get("foo")); + } + + @Test + public void requireThatPutAllWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + headers.add("foo", "bar"); + headers.add("bar", "baz"); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + assertEquals(Arrays.asList("baz"), headers.get("bar")); + + Map<String, List<String>> map = new HashMap<>(); + map.put("foo", Arrays.asList("baz", "cox")); + map.put("bar", Arrays.asList("cox")); + headers.putAll(map); + + assertEquals(Arrays.asList("baz", "cox"), headers.get("foo")); + assertEquals(Arrays.asList("cox"), headers.get("bar")); + } + + @Test + public void requireThatRemoveWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + headers.put("foo", Arrays.asList("bar", "baz")); + assertEquals(Arrays.asList("bar", "baz"), headers.get("foo")); + assertEquals(Arrays.asList("bar", "baz"), headers.remove("foo")); + assertNull(headers.get("foo")); + assertNull(headers.remove("foo")); + } + + @Test + public void requireThatRemoveStringWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + headers.put("foo", Arrays.asList("bar", "baz")); + assertEquals(Arrays.asList("bar", "baz"), headers.get("foo")); + assertTrue(headers.remove("foo", "bar")); + assertFalse(headers.remove("foo", "cox")); + assertEquals(Arrays.asList("baz"), headers.get("foo")); + assertTrue(headers.remove("foo", "baz")); + assertFalse(headers.remove("foo", "cox")); + assertNull(headers.get("foo")); + } + + @Test + public void requireThatClearWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + headers.add("foo", "bar"); + headers.add("bar", "baz"); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + assertEquals(Arrays.asList("baz"), headers.get("bar")); + headers.clear(); + assertNull(headers.get("foo")); + assertNull(headers.get("bar")); + } + + @Test + public void requireThatGetWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertNull(headers.get("foo")); + headers.add("foo", "bar"); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + } + + @Test + public void requireThatGetFirstWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertNull(headers.getFirst("foo")); + headers.add("foo", Arrays.asList("bar", "baz")); + assertEquals("bar", headers.getFirst("foo")); + } + + @Test + public void requireThatIsTrueWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertFalse(headers.isTrue("foo")); + headers.put("foo", Arrays.asList("true")); + assertTrue(headers.isTrue("foo")); + headers.put("foo", Arrays.asList("true", "true")); + assertTrue(headers.isTrue("foo")); + headers.put("foo", Arrays.asList("true", "false")); + assertFalse(headers.isTrue("foo")); + headers.put("foo", Arrays.asList("false", "true")); + assertFalse(headers.isTrue("foo")); + headers.put("foo", Arrays.asList("false", "false")); + assertFalse(headers.isTrue("foo")); + headers.put("foo", Arrays.asList("false")); + assertFalse(headers.isTrue("foo")); + } + + @Test + public void requireThatKeySetWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertTrue(headers.keySet().isEmpty()); + headers.add("foo", "bar"); + assertEquals(new HashSet<>(Arrays.asList("foo")), headers.keySet()); + headers.add("bar", "baz"); + assertEquals(new HashSet<>(Arrays.asList("foo", "bar")), headers.keySet()); + } + + @Test + public void requireThatValuesWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertTrue(headers.values().isEmpty()); + headers.add("foo", "bar"); + Collection<List<String>> values = headers.values(); + assertEquals(1, values.size()); + assertTrue(values.contains(Arrays.asList("bar"))); + + headers.add("bar", "baz"); + values = headers.values(); + assertEquals(2, values.size()); + assertTrue(values.contains(Arrays.asList("bar"))); + assertTrue(values.contains(Arrays.asList("baz"))); + } + + @Test + public void requireThatEntrySetWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertTrue(headers.entrySet().isEmpty()); + headers.put("foo", Arrays.asList("bar", "baz")); + + Set<Map.Entry<String, List<String>>> entries = headers.entrySet(); + assertEquals(1, entries.size()); + Map.Entry<String, List<String>> entry = entries.iterator().next(); + assertNotNull(entry); + assertEquals("foo", entry.getKey()); + assertEquals(Arrays.asList("bar", "baz"), entry.getValue()); + } + + @Test + public void requireThatEntriesWorksAsExpected() { + HeaderFields headers = new HeaderFields(); + assertTrue(headers.entries().isEmpty()); + headers.put("foo", Arrays.asList("bar", "baz")); + + List<Map.Entry<String, String>> entries = headers.entries(); + assertEquals(2, entries.size()); + + Map.Entry<String, String> entry = entries.get(0); + assertNotNull(entry); + assertEquals("foo", entry.getKey()); + assertEquals("bar", entry.getValue()); + + assertNotNull(entry = entries.get(1)); + assertEquals("foo", entry.getKey()); + assertEquals("baz", entry.getValue()); + } + + @Test + public void requireThatEntryIsUnmodifiable() { + HeaderFields headers = new HeaderFields(); + headers.put("foo", "bar"); + Map.Entry<String, String> entry = headers.entries().get(0); + try { + entry.setValue("baz"); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + @Test + public void requireThatEntriesAreUnmodifiable() { + HeaderFields headers = new HeaderFields(); + headers.put("foo", "bar"); + List<Map.Entry<String, String>> entries = headers.entries(); + try { + entries.add(new MyEntry()); + fail(); + } catch (UnsupportedOperationException e) { + + } + try { + entries.remove(new MyEntry()); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + @Test + public void requireThatEqualsWorksAsExpected() { + HeaderFields lhs = new HeaderFields(); + HeaderFields rhs = new HeaderFields(); + assertTrue(lhs.equals(rhs)); + lhs.add("foo", "bar"); + assertFalse(lhs.equals(rhs)); + rhs.add("foo", "bar"); + assertTrue(lhs.equals(rhs)); + } + + @Test + public void requireThatHashCodeWorksAsExpected() { + HeaderFields lhs = new HeaderFields(); + HeaderFields rhs = new HeaderFields(); + assertTrue(lhs.hashCode() == rhs.hashCode()); + lhs.add("foo", "bar"); + assertTrue(lhs.hashCode() != rhs.hashCode()); + rhs.add("foo", "bar"); + assertTrue(lhs.hashCode() == rhs.hashCode()); + } + + private static class MyEntry implements Map.Entry<String, String> { + + @Override + public String getKey() { + return "key"; + } + + @Override + public String getValue() { + return "value"; + } + + @Override + public String setValue(String value) { + return "value"; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/ProxyRequestHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/ProxyRequestHandlerTestCase.java new file mode 100644 index 00000000000..54a5a697791 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/ProxyRequestHandlerTestCase.java @@ -0,0 +1,583 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc; + +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ProxyRequestHandlerTestCase { + + @Test + public void requireThatRequestHandlerIsProxied() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Request request = newRequest(driver, requestHandler); + RequestHandler resolvedHandler = new ProxyRequestHandler(request.container().resolveHandler(request)); + MyResponseHandler responseHandler = MyResponseHandler.newEagerCompletion(); + resolvedHandler.handleRequest(request, responseHandler).close(null); + request.release(); + assertNotNull(requestHandler.handler); + resolvedHandler.handleTimeout(request, responseHandler); + assertTrue(requestHandler.timeout); + requestHandler.respond(); + + requestHandler.release(); + final ResourceReference resolvedHandlerReference = resolvedHandler.refer(); + assertTrue(driver.close()); // release installed ref + + assertFalse(requestHandler.destroyed); + resolvedHandlerReference.close(); + assertTrue(requestHandler.destroyed); + } + + @Test + public void requireThatRequestContentCompletedIsProxied() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = MyRequestHandler.newInstance(); + Request request = newRequest(driver, requestHandler); + ContentChannel resolvedContent = request.connect(MyResponseHandler.newEagerCompletion()); + request.release(); + + assertSame(request, requestHandler.request); + + ByteBuffer buf = ByteBuffer.allocate(69); + resolvedContent.write(buf, null); + assertSame(buf, requestHandler.content.writeBuf); + requestHandler.content.writeCompletion.completed(); + MyCompletion writeCompletion = new MyCompletion(); + resolvedContent.write(buf = ByteBuffer.allocate(69), writeCompletion); + assertSame(buf, requestHandler.content.writeBuf); + assertFalse(writeCompletion.completed); + assertNull(writeCompletion.failed); + requestHandler.content.writeCompletion.completed(); + assertTrue(writeCompletion.completed); + assertNull(writeCompletion.failed); + + MyCompletion closeCompletion = new MyCompletion(); + resolvedContent.close(closeCompletion); + assertTrue(requestHandler.content.closed); + assertFalse(closeCompletion.completed); + assertNull(writeCompletion.failed); + requestHandler.content.closeCompletion.completed(); + assertTrue(closeCompletion.completed); + assertNull(closeCompletion.failed); + + requestHandler.respond(); + assertTrue(driver.close()); + } + + @Test + public void requireThatRequestContentFailedIsProxied() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = MyRequestHandler.newInstance(); + Request request = newRequest(driver, requestHandler); + ContentChannel resolvedContent = request.connect(MyResponseHandler.newEagerCompletion()); + request.release(); + + assertSame(request, requestHandler.request); + + ByteBuffer buf = ByteBuffer.allocate(69); + resolvedContent.write(buf, null); + assertSame(buf, requestHandler.content.writeBuf); + requestHandler.content.writeCompletion.completed(); + MyCompletion writeCompletion = new MyCompletion(); + resolvedContent.write(buf = ByteBuffer.allocate(69), writeCompletion); + assertSame(buf, requestHandler.content.writeBuf); + assertFalse(writeCompletion.completed); + assertNull(writeCompletion.failed); + MyException writeFailed = new MyException(); + requestHandler.content.writeCompletion.failed(writeFailed); + assertFalse(writeCompletion.completed); + assertSame(writeFailed, writeCompletion.failed); + + MyCompletion closeCompletion = new MyCompletion(); + resolvedContent.close(closeCompletion); + assertTrue(requestHandler.content.closed); + assertFalse(closeCompletion.completed); + assertNull(closeCompletion.failed); + MyException closeFailed = new MyException(); + requestHandler.content.closeCompletion.failed(closeFailed); + assertFalse(writeCompletion.completed); + assertSame(closeFailed, closeCompletion.failed); + + requestHandler.respond(); + assertTrue(driver.close()); + } + + @Test + public void requireThatNullRequestContentIsProxied() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = MyRequestHandler.newNullContent(); + Request request = newRequest(driver, requestHandler); + request.connect(MyResponseHandler.newEagerCompletion()).close(null); + request.release(); + + requestHandler.respond(); + assertTrue(driver.close()); + } + + @Test + public void requireThatRequestWriteCompletionCanOnlyBeCalledOnce() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = MyRequestHandler.newInstance(); + Request request = newRequest(driver, requestHandler); + ContentChannel resolvedContent = request.connect(MyResponseHandler.newEagerCompletion()); + request.release(); + + CountingCompletionHandler completion = new CountingCompletionHandler(); + resolvedContent.write(ByteBuffer.allocate(0), completion); + assertEquals(0, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + requestHandler.content.writeCompletion.completed(); + assertEquals(1, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + try { + requestHandler.content.writeCompletion.completed(); + fail(); + } catch (IllegalStateException e) { + // ignore + } + assertEquals(1, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + try { + requestHandler.content.writeCompletion.failed(new Throwable()); + fail(); + } catch (IllegalStateException e) { + // ignore + } + assertEquals(1, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + + resolvedContent.close(null); + requestHandler.content.closeCompletion.completed(); + requestHandler.respond(); + assertTrue(driver.close()); + } + + @Test + public void requireThatRequestCloseCompletionCanOnlyBeCalledOnce() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = MyRequestHandler.newInstance(); + Request request = newRequest(driver, requestHandler); + ContentChannel resolvedContent = request.connect(MyResponseHandler.newEagerCompletion()); + request.release(); + + CountingCompletionHandler completion = new CountingCompletionHandler(); + resolvedContent.close(completion); + assertEquals(0, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + requestHandler.content.closeCompletion.completed(); + assertEquals(1, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + try { + requestHandler.content.closeCompletion.completed(); + fail(); + } catch (IllegalStateException e) { + // ignore + } + assertEquals(1, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + try { + requestHandler.content.closeCompletion.failed(new Throwable()); + fail(); + } catch (IllegalStateException e) { + // ignore + } + assertEquals(1, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + + requestHandler.respond(); + assertTrue(driver.close()); + } + + @Test + public void requireThatResponseContentCompletedIsProxied() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Request request = newRequest(driver, requestHandler); + MyResponseHandler responseHandler = MyResponseHandler.newInstance(); + request.connect(responseHandler).close(null); + request.release(); + Response response = new Response(Response.Status.OK); + ContentChannel resolvedContent = requestHandler.handler.handleResponse(response); + + assertSame(response, responseHandler.response); + + ByteBuffer buf = ByteBuffer.allocate(69); + resolvedContent.write(buf, null); + assertSame(buf, responseHandler.content.writeBuf); + responseHandler.content.writeCompletion.completed(); + MyCompletion writeCompletion = new MyCompletion(); + resolvedContent.write(buf = ByteBuffer.allocate(69), writeCompletion); + assertSame(buf, responseHandler.content.writeBuf); + assertFalse(writeCompletion.completed); + assertNull(writeCompletion.failed); + responseHandler.content.writeCompletion.completed(); + assertTrue(writeCompletion.completed); + assertNull(writeCompletion.failed); + + MyCompletion closeCompletion = new MyCompletion(); + resolvedContent.close(closeCompletion); + assertTrue(responseHandler.content.closed); + assertFalse(closeCompletion.completed); + assertNull(closeCompletion.failed); + responseHandler.content.closeCompletion.completed(); + assertTrue(closeCompletion.completed); + assertNull(closeCompletion.failed); + assertTrue(driver.close()); + } + + @Test + public void requireThatResponseContentFailedIsProxied() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Request request = newRequest(driver, requestHandler); + MyResponseHandler responseHandler = MyResponseHandler.newInstance(); + request.connect(responseHandler).close(null); + request.release(); + Response response = new Response(Response.Status.OK); + ContentChannel resolvedContent = requestHandler.handler.handleResponse(response); + + assertSame(response, responseHandler.response); + + ByteBuffer buf = ByteBuffer.allocate(69); + resolvedContent.write(buf, null); + assertSame(buf, responseHandler.content.writeBuf); + responseHandler.content.writeCompletion.completed(); + MyCompletion writeCompletion = new MyCompletion(); + resolvedContent.write(buf = ByteBuffer.allocate(69), writeCompletion); + assertSame(buf, responseHandler.content.writeBuf); + assertFalse(writeCompletion.completed); + assertNull(writeCompletion.failed); + MyException writeFailed = new MyException(); + responseHandler.content.writeCompletion.failed(writeFailed); + assertFalse(writeCompletion.completed); + assertSame(writeFailed, writeCompletion.failed); + + MyCompletion closeCompletion = new MyCompletion(); + resolvedContent.close(closeCompletion); + assertTrue(responseHandler.content.closed); + assertFalse(closeCompletion.completed); + assertNull(closeCompletion.failed); + MyException closeFailed = new MyException(); + responseHandler.content.closeCompletion.failed(closeFailed); + assertFalse(closeCompletion.completed); + assertSame(closeFailed, closeCompletion.failed); + assertTrue(driver.close()); + } + + @Test + public void requireThatNullResponseContentIsProxied() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Request request = newRequest(driver, requestHandler); + ResponseHandler responseHandler = new ResponseHandler() { + + @Override + public ContentChannel handleResponse(Response response) { + return null; + } + }; + request.connect(responseHandler).close(null); + requestHandler.handler.handleResponse(new Response(Response.Status.OK)).close(null); + request.release(); + assertTrue(driver.close()); + } + + @Test + public void requireThatResponseWriteCompletionCanOnlyBeCalledOnce() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + MyResponseHandler responseHandler = MyResponseHandler.newInstance(); + Request request = newRequest(driver, requestHandler); + request.connect(responseHandler).close(null); + request.release(); + ContentChannel resolvedContent = requestHandler.handler.handleResponse(new Response(Response.Status.OK)); + + CountingCompletionHandler completion = new CountingCompletionHandler(); + resolvedContent.write(ByteBuffer.allocate(0), completion); + assertEquals(0, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + responseHandler.content.writeCompletion.completed(); + assertEquals(1, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + try { + responseHandler.content.writeCompletion.completed(); + fail(); + } catch (IllegalStateException e) { + // ignore + } + assertEquals(1, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + try { + responseHandler.content.writeCompletion.failed(new Throwable()); + fail(); + } catch (IllegalStateException e) { + // ignore + } + assertEquals(1, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + + resolvedContent.close(null); + responseHandler.content.closeCompletion.completed(); + assertTrue(driver.close()); + } + + @Test + public void requireThatResponseCloseCompletionCanOnlyBeCalledOnce() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + MyResponseHandler responseHandler = MyResponseHandler.newInstance(); + Request request = newRequest(driver, requestHandler); + request.connect(responseHandler).close(null); + request.release(); + ContentChannel resolvedContent = requestHandler.handler.handleResponse(new Response(Response.Status.OK)); + + CountingCompletionHandler completion = new CountingCompletionHandler(); + resolvedContent.close(completion); + assertEquals(0, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + responseHandler.content.closeCompletion.completed(); + assertEquals(1, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + try { + responseHandler.content.closeCompletion.completed(); + fail(); + } catch (IllegalStateException e) { + // ignore + } + assertEquals(1, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + try { + responseHandler.content.closeCompletion.failed(new Throwable()); + fail(); + } catch (IllegalStateException e) { + // ignore + } + assertEquals(1, completion.numCompleted.get()); + assertEquals(0, completion.numFailed.get()); + assertTrue(driver.close()); + } + + @Test + public void requireThatUncaughtCompletionFailureIsLogged() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = MyRequestHandler.newInstance(); + Request request = newRequest(driver, requestHandler); + ContentChannel resolvedContent = request.connect(MyResponseHandler.newEagerCompletion()); + request.release(); + + MyLogHandler logHandler = new MyLogHandler(); + Logger.getLogger(ProxyRequestHandler.class.getName()).addHandler(logHandler); + + resolvedContent.write(ByteBuffer.allocate(69), null); + MyException writeFailed = new MyException(); + requestHandler.content.writeCompletion.failed(writeFailed); + assertNotNull(logHandler.record); + assertSame(writeFailed, logHandler.record.getThrown()); + + resolvedContent.close(null); + MyException closeFailed = new MyException(); + requestHandler.content.closeCompletion.failed(closeFailed); + assertNotNull(logHandler.record); + assertSame(closeFailed, logHandler.record.getThrown()); + + requestHandler.respond(); + assertTrue(driver.close()); + } + + private static Request newRequest(TestDriver driver, RequestHandler requestHandler) { + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind("http://host/path", requestHandler); + driver.activateContainer(builder); + return new Request(driver, URI.create("http://host/path")); + } + + private static class MyException extends RuntimeException { + + } + + private static class MyCompletion implements CompletionHandler { + + boolean completed = false; + Throwable failed = null; + + @Override + public void completed() { + completed = true; + } + + @Override + public void failed(Throwable t) { + failed = t; + } + } + + private static class MyContent implements ContentChannel { + + final boolean eagerCompletion; + CompletionHandler writeCompletion = null; + CompletionHandler closeCompletion = null; + ByteBuffer writeBuf = null; + boolean closed = false; + + MyContent(boolean eagerCompletion) { + this.eagerCompletion = eagerCompletion; + } + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + writeBuf = buf; + writeCompletion = handler; + if (eagerCompletion) { + writeCompletion.completed(); + } + } + + @Override + public void close(CompletionHandler handler) { + closed = true; + closeCompletion = handler; + if (eagerCompletion) { + closeCompletion.completed(); + } + } + + static MyContent newInstance() { + return new MyContent(false); + } + + static MyContent newEagerCompletion() { + return new MyContent(true); + } + } + + private static class MyRequestHandler extends AbstractResource implements RequestHandler { + + final MyContent content; + Request request = null; + ResponseHandler handler = null; + boolean timeout = false; + boolean destroyed = false; + + MyRequestHandler(MyContent content) { + this.content = content; + } + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + this.request = request; + this.handler = handler; + return content; + } + + @Override + public void handleTimeout(Request request, ResponseHandler handler) { + timeout = true; + } + + @Override + public void destroy() { + destroyed = true; + } + + void respond() { + handler.handleResponse(new Response(Response.Status.OK)).close(null); + } + + static MyRequestHandler newInstance() { + return new MyRequestHandler(MyContent.newInstance()); + } + + static MyRequestHandler newEagerCompletion() { + return new MyRequestHandler(MyContent.newEagerCompletion()); + } + + static MyRequestHandler newNullContent() { + return new MyRequestHandler(null); + } + } + + private static class MyResponseHandler implements ResponseHandler { + + final MyContent content; + Response response; + + MyResponseHandler(MyContent content) { + this.content = content; + } + + @Override + public ContentChannel handleResponse(Response response) { + this.response = response; + return content; + } + + static MyResponseHandler newInstance() { + return new MyResponseHandler(MyContent.newInstance()); + } + + static MyResponseHandler newEagerCompletion() { + return new MyResponseHandler(MyContent.newEagerCompletion()); + } + } + + private static class MyLogHandler extends Handler { + + LogRecord record; + + @Override + public void publish(LogRecord record) { + this.record = record; + } + + @Override + public void flush() { + + } + + @Override + public void close() throws SecurityException { + + } + } + + private static class CountingCompletionHandler implements CompletionHandler { + + final AtomicInteger numCompleted = new AtomicInteger(0); + final AtomicInteger numFailed = new AtomicInteger(0); + + @Override + public void completed() { + numCompleted.incrementAndGet(); + } + + @Override + public void failed(Throwable t) { + numFailed.incrementAndGet(); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/ReferencedResourceTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/ReferencedResourceTestCase.java new file mode 100644 index 00000000000..4337d8b8c6c --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/ReferencedResourceTestCase.java @@ -0,0 +1,34 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.sameInstance; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +/** + * @author <a href="mailto:bakksjo@yahoo-inc.com">Oyvind Bakksjo</a> + */ +public class ReferencedResourceTestCase { + @Test + public void requireThatGettersMatchConstructor() { + final SharedResource resource = mock(SharedResource.class); + final ResourceReference reference = mock(ResourceReference.class); + final ReferencedResource<SharedResource> referencedResource = new ReferencedResource<>(resource, reference); + assertThat(referencedResource.getResource(), is(sameInstance(resource))); + assertThat(referencedResource.getReference(), is(sameInstance(reference))); + } + + @Test + public void requireThatCloseCallsReferenceClose() { + final SharedResource resource = mock(SharedResource.class); + final ResourceReference reference = mock(ResourceReference.class); + final ReferencedResource<SharedResource> referencedResource = new ReferencedResource<>(resource, reference); + referencedResource.close(); + verify(reference, times(1)).close(); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/ReferencesTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/ReferencesTestCase.java new file mode 100644 index 00000000000..d8cbe36c04f --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/ReferencesTestCase.java @@ -0,0 +1,27 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +/** + * @author <a href="mailto:bakksjo@yahoo-inc.com">Oyvind Bakksjo</a> + */ +public class ReferencesTestCase { + @Test + public void requireThatFromResourceCallsReleaseOnResource() { + final SharedResource resource = mock(SharedResource.class); + final ResourceReference reference = References.fromResource(resource); + reference.close(); + verify(resource, times(1)).release(); + } + + @Test + public void requireThatNoopReferenceCanBeCalledMultipleTimes() { + References.NOOP_REFERENCE.close(); + References.NOOP_REFERENCE.close(); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/RequestTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/RequestTestCase.java new file mode 100644 index 00000000000..fe5af79a6d3 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/RequestTestCase.java @@ -0,0 +1,381 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Key; +import com.yahoo.jdisc.application.BindingMatch; +import com.yahoo.jdisc.application.UriPattern; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.service.BindingSetNotFoundException; +import com.yahoo.jdisc.service.CurrentContainer; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class RequestTestCase { + + @Test + public void requireThatAccessorsWork() throws BindingSetNotFoundException { + MyTimer timer = new MyTimer(); + timer.currentTime = 69; + + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(timer); + driver.activateContainer(driver.newContainerBuilder()); + Request request = new Request(driver, URI.create("http://foo/bar")); + assertNotNull(request); + assertEquals(URI.create("http://foo/bar"), request.getUri()); + request.setUri(URI.create("http://baz/cox")); + assertEquals(URI.create("http://baz/cox"), request.getUri()); + assertTrue(request.isServerRequest()); + request.setServerRequest(false); + assertFalse(request.isServerRequest()); + assertEquals(69, request.creationTime(TimeUnit.MILLISECONDS)); + assertNull(request.getTimeout(TimeUnit.MILLISECONDS)); + request.setTimeout(10, TimeUnit.MILLISECONDS); + assertNotNull(request.getTimeout(TimeUnit.MILLISECONDS)); + assertEquals(10, request.timeRemaining(TimeUnit.MILLISECONDS).longValue()); + assertTrue(request.context().isEmpty()); + assertTrue(request.headers().isEmpty()); + TimeoutManager timeoutManager = new MyTimeoutManager(); + request.setTimeoutManager(timeoutManager); + assertSame(timeoutManager, request.getTimeoutManager()); + request.release(); + assertTrue(driver.close()); + } + + @Test + public void requireThatCancelWorks() { + MyTimer timer = new MyTimer(); + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(timer); + Request request = newRequest(driver); + assertFalse(request.isCancelled()); + request.cancel(); + assertTrue(request.isCancelled()); + request.release(); + assertTrue(driver.close()); + } + + @Test + public void requireThatDefaultTimeoutIsInfinite() { + MyTimer timer = new MyTimer(); + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(timer); + Request request = newRequest(driver); + assertNull(request.getTimeout(TimeUnit.MILLISECONDS)); + assertNull(request.timeRemaining(TimeUnit.MILLISECONDS)); + assertFalse(request.isCancelled()); + timer.currentTime = Long.MAX_VALUE; + assertFalse(request.isCancelled()); + request.release(); + assertTrue(driver.close()); + } + + @Test + public void requireThatTimeRemainingUsesTimer() { + MyTimer timer = new MyTimer(); + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(timer); + Request request = newRequest(driver); + request.setTimeout(10, TimeUnit.MILLISECONDS); + for (timer.currentTime = 0; timer.currentTime <= request.getTimeout(TimeUnit.MILLISECONDS); + ++timer.currentTime) + { + assertEquals(request.getTimeout(TimeUnit.MILLISECONDS) - timer.currentTime, + request.timeRemaining(TimeUnit.MILLISECONDS).longValue()); + } + request.release(); + assertTrue(driver.close()); + } + + @Test + public void requireThatTimeoutCausesCancel() { + MyTimer timer = new MyTimer(); + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(timer); + Request request = newRequest(driver); + request.setTimeout(10, TimeUnit.MILLISECONDS); + assertFalse(request.isCancelled()); + timer.currentTime = 10; + assertTrue(request.isCancelled()); + request.release(); + assertTrue(driver.close()); + } + + @Test + public void requireThatCancelIsTrueIfParentIsCancelled() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + Request parent = newRequest(driver); + Request child = new Request(parent, URI.create("http://localhost/")); + parent.cancel(); + assertTrue(child.isCancelled()); + parent.release(); + child.release(); + assertTrue(driver.close()); + } + + @Test + public void requireThatDestroyReleasesContainer() { + final MyContainer container = new MyContainer(); + Request request = new Request(new CurrentContainer() { + + @Override + public Container newReference(URI uri) { + return container; + } + }, URI.create("http://localhost/")); + assertEquals(1, container.refCount); + request.release(); + assertEquals(0, container.refCount); + } + + @Test + public void requireThatServerConnectResolvesToServerBinding() { + MyContainer container = new MyContainer(); + Request request = new Request(container, URI.create("http://localhost/")); + request.connect(new MyResponseHandler()); + assertNotNull(container.asServer); + assertTrue(container.asServer); + } + + @Test + public void requireThatClientConnectResolvesToClientBinding() { + MyContainer container = new MyContainer(); + Request serverReq = new Request(container, URI.create("http://localhost/")); + Request clientReq = new Request(serverReq, URI.create("http://localhost/")); + clientReq.connect(new MyResponseHandler()); + assertNotNull(container.asServer); + assertFalse(container.asServer); + } + + @Test + public void requireThatNullTimeoutManagerThrowsException() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + Request request = newRequest(driver); + + try { + request.setTimeoutManager(null); + fail(); + } catch (NullPointerException e) { + + } + + request.release(); + assertTrue(driver.close()); + } + + @Test + public void requireThatTimeoutManagerCanNotBeReplaced() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + Request request = newRequest(driver); + + TimeoutManager manager = new MyTimeoutManager(); + request.setTimeoutManager(manager); + try { + request.setTimeoutManager(manager); + fail(); + } catch (IllegalStateException e) { + assertEquals("Timeout manager already set.", e.getMessage()); + } + + request.release(); + assertTrue(driver.close()); + } + + @Test + public void requireThatSetTimeoutCallsTimeoutManager() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + Request request = newRequest(driver); + + MyTimeoutManager timeoutManager = new MyTimeoutManager(); + request.setTimeoutManager(timeoutManager); + request.setTimeout(6, TimeUnit.SECONDS); + assertEquals(6000, timeoutManager.timeoutMillis); + + request.release(); + assertTrue(driver.close()); + } + + @Test + public void requireThatSetTimeoutManagerPropagatesCurrentTimeout() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + Request request = newRequest(driver); + + MyTimeoutManager timeoutManager = new MyTimeoutManager(); + request.setTimeout(6, TimeUnit.SECONDS); + request.setTimeoutManager(timeoutManager); + assertEquals(6000, timeoutManager.timeoutMillis); + + request.release(); + assertTrue(driver.close()); + } + + @Test + public void requireThatUriIsNormalized() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + driver.activateContainer(driver.newContainerBuilder()); + + assertUri(driver, "http://host/foo", "http://host/foo"); + assertUri(driver, "http://host/./foo", "http://host/foo"); + assertUri(driver, "http://host/././foo", "http://host/foo"); + assertUri(driver, "http://host/foo/", "http://host/foo/"); + assertUri(driver, "http://host/foo/.", "http://host/foo/"); + assertUri(driver, "http://host/foo/./", "http://host/foo/"); + assertUri(driver, "http://host/foo/./.", "http://host/foo/"); + assertUri(driver, "http://host/foo/././", "http://host/foo/"); + assertUri(driver, "http://host/foo/..", "http://host/"); + assertUri(driver, "http://host/foo/../", "http://host/"); + assertUri(driver, "http://host/foo/../bar", "http://host/bar"); + assertUri(driver, "http://host/foo/../bar/", "http://host/bar/"); + assertUri(driver, "http://host//foo//", "http://host/foo/"); + assertUri(driver, "http://host///foo///", "http://host/foo/"); + assertUri(driver, "http://host///foo///bar///", "http://host/foo/bar/"); + + assertTrue(driver.close()); + } + + private static void assertUri(CurrentContainer container, String requestUri, String expectedUri) { + Request serverReq = new Request(container, URI.create(requestUri)); + assertEquals(expectedUri, serverReq.getUri().toString()); + + serverReq.setUri(URI.create(requestUri)); + assertEquals(expectedUri, serverReq.getUri().toString()); + + Request clientReq = new Request(serverReq, URI.create(requestUri)); + assertEquals(expectedUri, clientReq.getUri().toString()); + + serverReq.release(); + clientReq.release(); + } + + private static Request newRequest(TestDriver driver) { + driver.activateContainer(driver.newContainerBuilder()); + return new Request(driver, URI.create("http://host/path")); + } + + private static class MyTimer extends AbstractModule implements Timer { + + long currentTime = 0; + + @Override + public long currentTimeMillis() { + return currentTime; + } + + @Override + protected void configure() { + bind(Timer.class).toInstance(this); + } + } + + private static class MyContainer implements CurrentContainer, Container { + + Boolean asServer = null; + int refCount = 1; + + @Override + public Container newReference(URI uri) { + return this; + } + + @Override + public RequestHandler resolveHandler(Request request) { + this.asServer = request.isServerRequest(); + RequestHandler requestHandler = new MyRequestHandler(); + request.setBindingMatch(new BindingMatch<>( + new UriPattern("http://*/*").match(request.getUri()), + requestHandler)); + return requestHandler; + } + + @Override + public <T> T getInstance(Key<T> key) { + return Guice.createInjector().getInstance(key); + } + + @Override + public <T> T getInstance(Class<T> type) { + return Guice.createInjector().getInstance(type); + } + + @Override + public ResourceReference refer() { + ++refCount; + return new ResourceReference() { + @Override + public void close() { + --refCount; + } + }; + } + + @Override + public void release() { + --refCount; + } + + @Override + public long currentTimeMillis() { + return 0; + } + } + + private static class MyRequestHandler extends NoopSharedResource implements RequestHandler { + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + return new MyContentChannel(); + } + + @Override + public void handleTimeout(Request request, ResponseHandler handler) { + + } + } + + private static class MyResponseHandler implements ResponseHandler { + + @Override + public ContentChannel handleResponse(Response response) { + return null; + } + } + + private static class MyContentChannel implements ContentChannel { + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + + } + + @Override + public void close(CompletionHandler handler) { + + } + } + + private static class MyTimeoutManager implements TimeoutManager { + + long timeoutMillis; + + @Override + public void scheduleTimeout(Request request) { + timeoutMillis = request.getTimeout(TimeUnit.MILLISECONDS); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/ResponseTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/ResponseTestCase.java new file mode 100644 index 00000000000..303d6b0dc55 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/ResponseTestCase.java @@ -0,0 +1,86 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ResponseTestCase { + + @Test + public void requireThatAccessorsWork() { + Response response = new Response(69); + assertEquals(69, response.getStatus()); + response.setStatus(96); + assertEquals(96, response.getStatus()); + Throwable t = new Throwable(); + response.setError(t); + assertSame(t, response.getError()); + assertTrue(response.context().isEmpty()); + assertTrue(response.headers().isEmpty()); + } + + @Test + public void requireThatStatusCodesDoNotChange() { + assertEquals(100, Response.Status.CONTINUE); + assertEquals(101, Response.Status.SWITCHING_PROTOCOLS); + assertEquals(102, Response.Status.PROCESSING); + + assertEquals(200, Response.Status.OK); + assertEquals(201, Response.Status.CREATED); + assertEquals(202, Response.Status.ACCEPTED); + assertEquals(203, Response.Status.NON_AUTHORITATIVE_INFORMATION); + assertEquals(204, Response.Status.NO_CONTENT); + assertEquals(205, Response.Status.RESET_CONTENT); + assertEquals(206, Response.Status.PARTIAL_CONTENT); + assertEquals(207, Response.Status.MULTI_STATUS); + + assertEquals(300, Response.Status.MULTIPLE_CHOICES); + assertEquals(301, Response.Status.MOVED_PERMANENTLY); + assertEquals(302, Response.Status.FOUND); + assertEquals(303, Response.Status.SEE_OTHER); + assertEquals(304, Response.Status.NOT_MODIFIED); + assertEquals(305, Response.Status.USE_PROXY); + assertEquals(307, Response.Status.TEMPORARY_REDIRECT); + + assertEquals(400, Response.Status.BAD_REQUEST); + assertEquals(401, Response.Status.UNAUTHORIZED); + assertEquals(402, Response.Status.PAYMENT_REQUIRED); + assertEquals(403, Response.Status.FORBIDDEN); + assertEquals(404, Response.Status.NOT_FOUND); + assertEquals(405, Response.Status.METHOD_NOT_ALLOWED); + assertEquals(406, Response.Status.NOT_ACCEPTABLE); + assertEquals(407, Response.Status.PROXY_AUTHENTICATION_REQUIRED); + assertEquals(408, Response.Status.REQUEST_TIMEOUT); + assertEquals(409, Response.Status.CONFLICT); + assertEquals(410, Response.Status.GONE); + assertEquals(411, Response.Status.LENGTH_REQUIRED); + assertEquals(412, Response.Status.PRECONDITION_FAILED); + assertEquals(413, Response.Status.REQUEST_TOO_LONG); + assertEquals(414, Response.Status.REQUEST_URI_TOO_LONG); + assertEquals(415, Response.Status.UNSUPPORTED_MEDIA_TYPE); + assertEquals(416, Response.Status.REQUESTED_RANGE_NOT_SATISFIABLE); + assertEquals(417, Response.Status.EXPECTATION_FAILED); + assertEquals(419, Response.Status.INSUFFICIENT_SPACE_ON_RESOURCE); + assertEquals(420, Response.Status.METHOD_FAILURE); + assertEquals(422, Response.Status.UNPROCESSABLE_ENTITY); + assertEquals(423, Response.Status.LOCKED); + assertEquals(424, Response.Status.FAILED_DEPENDENCY); + + assertEquals(505, Response.Status.VERSION_NOT_SUPPORTED); + assertEquals(500, Response.Status.INTERNAL_SERVER_ERROR); + assertEquals(501, Response.Status.NOT_IMPLEMENTED); + assertEquals(502, Response.Status.BAD_GATEWAY); + assertEquals(503, Response.Status.SERVICE_UNAVAILABLE); + assertEquals(504, Response.Status.GATEWAY_TIMEOUT); + assertEquals(505, Response.Status.VERSION_NOT_SUPPORTED); + assertEquals(507, Response.Status.INSUFFICIENT_STORAGE); + } + +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/AbstractApplicationTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/AbstractApplicationTestCase.java new file mode 100644 index 00000000000..da5f046ef1f --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/AbstractApplicationTestCase.java @@ -0,0 +1,98 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import com.google.inject.Inject; +import com.yahoo.jdisc.service.CurrentContainer; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class AbstractApplicationTestCase { + + @Test + public void requireThatContainerApiIsAvailable() { + TestDriver driver = TestDriver.newInjectedApplicationInstance(MyApplication.class); + MyApplication app = (MyApplication)driver.application(); + app.activateContainer(app.newContainerBuilder()); + assertNotNull(app.container()); + assertTrue(driver.close()); + } + + @Test + public void requireThatDestroySignalsTermination() { + TestDriver driver = TestDriver.newInjectedApplicationInstance(MyApplication.class); + MyApplication app = (MyApplication)driver.application(); + assertFalse(app.isTerminated()); + assertTrue(driver.close()); + assertTrue(app.isTerminated()); + } + + @Test + public void requireThatTerminationCanBeWaitedForWithTimeout() throws InterruptedException { + TestDriver driver = TestDriver.newInjectedApplicationInstance(MyApplication.class); + final MyApplication app = (MyApplication)driver.application(); + final CountDownLatch latch = new CountDownLatch(1); + Executors.newSingleThreadExecutor().execute(new Runnable() { + + @Override + public void run() { + try { + app.awaitTermination(600, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + latch.countDown(); + } + }); + assertFalse(latch.await(100, TimeUnit.MILLISECONDS)); + assertTrue(driver.close()); + assertTrue(latch.await(600, TimeUnit.SECONDS)); + } + + @Test + public void requireThatTerminationCanBeWaitedForWithoutTimeout() throws InterruptedException { + TestDriver driver = TestDriver.newInjectedApplicationInstance(MyApplication.class); + final MyApplication app = (MyApplication)driver.application(); + final CountDownLatch latch = new CountDownLatch(1); + Executors.newSingleThreadExecutor().execute(new Runnable() { + + @Override + public void run() { + try { + app.awaitTermination(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + latch.countDown(); + } + }); + assertFalse(latch.await(100, TimeUnit.MILLISECONDS)); + assertTrue(driver.close()); + assertTrue(latch.await(600, TimeUnit.SECONDS)); + } + + private static class MyApplication extends AbstractApplication { + + @Inject + public MyApplication(BundleInstaller bundleInstaller, ContainerActivator activator, + CurrentContainer container) { + super(bundleInstaller, activator, container); + } + + @Override + public void start() { + + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ApplicationNotReadyTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ApplicationNotReadyTestCase.java new file mode 100644 index 00000000000..1351e717015 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ApplicationNotReadyTestCase.java @@ -0,0 +1,53 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import com.google.inject.Inject; +import com.google.inject.ProvisionException; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ApplicationNotReadyTestCase { + + @Test + public void requireThatExceptionIsThrown() { + try { + TestDriver.newInjectedApplicationInstanceWithoutOsgi(MyApplication.class); + fail(); + } catch (ProvisionException e) { + Throwable t = e.getCause(); + assertNotNull(t); + assertTrue(t instanceof ApplicationNotReadyException); + } + } + + private static class MyApplication implements Application { + + @Inject + MyApplication(ContainerActivator activator) { + activator.activateContainer(activator.newContainerBuilder()); + } + + @Override + public void start() { + + } + + @Override + public void stop() { + + } + + @Override + public void destroy() { + + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingMatchTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingMatchTestCase.java new file mode 100644 index 00000000000..59d0535f99e --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingMatchTestCase.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.jdisc.application; + +import org.junit.Test; + +import java.net.URI; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class BindingMatchTestCase { + + @Test + public void requireThatAccessorsWork() { + Object obj = new Object(); + BindingMatch<Object> match = new BindingMatch<>( + new UriPattern("http://*/*").match(URI.create("http://localhost:69/status.html")), + obj); + assertSame(obj, match.target()); + assertEquals(3, match.groupCount()); + assertEquals("localhost", match.group(0)); + assertEquals("69", match.group(1)); + assertEquals("status.html", match.group(2)); + } + + @Test + public void requireThatConstructorArgumentsCanNotBeNull() { + try { + new BindingMatch<>(null, null); + fail(); + } catch (NullPointerException e) { + + } + try { + new BindingMatch<>(new UriPattern("http://*/*").match(URI.create("http://localhost/")), null); + fail(); + } catch (NullPointerException e) { + + } + try { + new BindingMatch<>(null, new Object()); + fail(); + } catch (NullPointerException e) { + + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingRepositoryTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingRepositoryTestCase.java new file mode 100644 index 00000000000..aa9bc783b74 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingRepositoryTestCase.java @@ -0,0 +1,181 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import com.yahoo.jdisc.NoopSharedResource; +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.test.NonWorkingRequestHandler; +import org.junit.Test; + +import java.net.URI; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class BindingRepositoryTestCase { + + @Test + public void requireThatRepositoryCanBeActivated() { + BindingRepository<Object> bindings = new BindingRepository<>(); + bindings.bind("http://host/path", new Object()); + + BindingSet<Object> set = bindings.activate(); + assertNotNull(set); + Iterator<Map.Entry<UriPattern, Object>> it = set.iterator(); + assertNotNull(it); + assertTrue(it.hasNext()); + assertNotNull(it.next()); + assertFalse(it.hasNext()); + } + + @Test + public void requireThatActivationIsSnapshotOfRepository() { + BindingRepository<Object> bindings = new BindingRepository<>(); + bindings.bind("http://host/path", new Object()); + + BindingSet<Object> set = bindings.activate(); + assertNotNull(set); + bindings.clear(); + + Iterator<Map.Entry<UriPattern, Object>> it = set.iterator(); + assertNotNull(it); + assertTrue(it.hasNext()); + assertNotNull(it.next()); + assertFalse(it.hasNext()); + } + + @Test + public void requireThatObjectsCanBeBound() { + BindingRepository<Object> bindings = new BindingRepository<>(); + Object foo = new Object(); + Object bar = new Object(); + bindings.bind("http://host/foo", foo); + bindings.bind("http://host/bar", bar); + + Iterator<Map.Entry<UriPattern, Object>> it = bindings.activate().iterator(); + assertNotNull(it); + assertTrue(it.hasNext()); + Map.Entry<UriPattern, Object> entry = it.next(); + assertNotNull(entry); + assertEquals(new UriPattern("http://host/foo"), entry.getKey()); + assertSame(foo, entry.getValue()); + assertTrue(it.hasNext()); + assertNotNull(entry = it.next()); + assertEquals(new UriPattern("http://host/bar"), entry.getKey()); + assertSame(bar, entry.getValue()); + assertFalse(it.hasNext()); + } + + @Test + public void requireThatPatternCannotBeStolen() { + final String pattern = "http://host/path"; + final RequestHandler originallyBoundHandler = new NonWorkingRequestHandler(); + + BindingRepository<Object> bindings = new BindingRepository<>(); + bindings.bind(pattern, originallyBoundHandler); + bindings.bind(pattern, new PatternStealingRequestHandler()); + + BindingSet bindingSet = bindings.activate(); + assertEquals(originallyBoundHandler, bindingSet.resolve(URI.create(pattern))); + } + + @Test + public void requireThatBindAllMethodWorks() { + Object foo = new Object(); + Object bar = new Object(); + Object baz = new Object(); + + Map<String, Object> toAdd = new HashMap<>(); + toAdd.put("http://host/foo", foo); + toAdd.put("http://host/bar", bar); + + BindingRepository<Object> addTo = new BindingRepository<>(); + addTo.bind("http://host/baz", baz); + addTo.bindAll(toAdd); + + Iterator<Map.Entry<UriPattern, Object>> it = addTo.activate().iterator(); + Map.Entry<UriPattern, Object> entry = it.next(); + assertNotNull(entry); + assertEquals(new UriPattern("http://host/foo"), entry.getKey()); + assertSame(foo, entry.getValue()); + assertTrue(it.hasNext()); + assertNotNull(entry = it.next()); + assertEquals(new UriPattern("http://host/baz"), entry.getKey()); + assertSame(baz, entry.getValue()); + assertTrue(it.hasNext()); + assertNotNull(entry = it.next()); + assertEquals(new UriPattern("http://host/bar"), entry.getKey()); + assertSame(bar, entry.getValue()); + assertFalse(it.hasNext()); + } + + @Test + public void requireThatPutAllMethodWorks() { + Object foo = new Object(); + Object bar = new Object(); + Object baz = new Object(); + + BindingRepository<Object> toAdd = new BindingRepository<>(); + toAdd.bind("http://host/foo", foo); + toAdd.bind("http://host/bar", bar); + + BindingRepository<Object> addTo = new BindingRepository<>(); + addTo.bind("http://host/baz", baz); + addTo.putAll(toAdd); + + Iterator<Map.Entry<UriPattern, Object>> it = addTo.activate().iterator(); + assertTrue(it.hasNext()); + Map.Entry<UriPattern, Object> entry = it.next(); + assertNotNull(entry); + assertEquals(new UriPattern("http://host/foo"), entry.getKey()); + assertSame(foo, entry.getValue()); + assertTrue(it.hasNext()); + assertNotNull(entry = it.next()); + assertEquals(new UriPattern("http://host/baz"), entry.getKey()); + assertSame(baz, entry.getValue()); + assertTrue(it.hasNext()); + assertNotNull(entry = it.next()); + assertEquals(new UriPattern("http://host/bar"), entry.getKey()); + assertSame(bar, entry.getValue()); + assertFalse(it.hasNext()); + } + + @Test + public void requireThatPutNullThrowsException() { + try { + new BindingRepository<>().put(null, new Object()); + fail(); + } catch (NullPointerException e) { + + } + try { + new BindingRepository<>().put(new UriPattern("http://host/foo"), null); + fail(); + } catch (NullPointerException e) { + + } + } + + static class PatternStealingRequestHandler extends NoopSharedResource implements RequestHandler { + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + throw new UnsupportedOperationException(); + } + + @Override + public void handleTimeout(Request request, ResponseHandler handler) { } + + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingSetTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingSetTestCase.java new file mode 100644 index 00000000000..7ff2aa6908b --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BindingSetTestCase.java @@ -0,0 +1,506 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.test.NonWorkingRequestHandler; +import static com.yahoo.vespa.defaults.Defaults.getDefaults; +import org.junit.Test; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class BindingSetTestCase { + + @Test + public void requireThatAccessorsWork() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host/foo"), foo); + RequestHandler bar = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host/bar"), bar); + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + + Iterator<Map.Entry<UriPattern, RequestHandler>> it = bindings.iterator(); + assertNotNull(it); + assertTrue(it.hasNext()); + Map.Entry<UriPattern, RequestHandler> entry = it.next(); + assertNotNull(entry); + assertEquals(new UriPattern("http://host/foo"), entry.getKey()); + assertSame(foo, entry.getValue()); + assertTrue(it.hasNext()); + assertNotNull(entry = it.next()); + assertEquals(new UriPattern("http://host/bar"), entry.getKey()); + assertSame(bar, entry.getValue()); + assertFalse(it.hasNext()); + } + + @Test + public void requireThatSimpleResolutionWorks() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host/foo"), foo); + RequestHandler bar = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host/bar"), bar); + + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + BindingMatch<RequestHandler> match = bindings.match(URI.create("http://host/foo")); + assertNotNull(match); + assertEquals(0, match.groupCount()); + assertSame(foo, match.target()); + assertSame(foo, bindings.resolve(URI.create("http://host/foo"))); + + assertNotNull(match = bindings.match(URI.create("http://host/bar"))); + assertEquals(0, match.groupCount()); + assertSame(bar, match.target()); + assertSame(bar, bindings.resolve(URI.create("http://host/bar"))); + } + + @Test + public void requireThatPatternResolutionWorks() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host/*"), foo); + RequestHandler bar = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host/path"), bar); + + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + BindingMatch<RequestHandler> match = bindings.match(URI.create("http://host/anon")); + assertNotNull(match); + assertEquals(1, match.groupCount()); + assertEquals("anon", match.group(0)); + assertSame(foo, match.target()); + assertSame(foo, bindings.resolve(URI.create("http://host/anon"))); + + assertNotNull(match = bindings.match(URI.create("http://host/path"))); + assertEquals(0, match.groupCount()); + assertSame(bar, match.target()); + assertSame(bar, bindings.resolve(URI.create("http://host/path"))); + } + + @Test + public void requireThatPatternResolutionWorksForWildCards() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host:*/bar"), foo); + RequestHandler bob = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://*abc:*/*bar"), bob); + RequestHandler car = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("*://*:21/*"), car); + + + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + BindingMatch<RequestHandler> match = bindings.match(URI.create("http://host:8080/bar")); + assertNotNull(match); + assertEquals(1, match.groupCount()); + assertEquals("8080", match.group(0)); + assertSame(foo, match.target()); + assertSame(foo, bindings.resolve(URI.create("http://host:8080/bar"))); + + match = bindings.match(URI.create("http://host:8080/foo/bar")); + assertNull(match); + + match = bindings.match(URI.create("http://xyzabc:8080/pqrbar")); + assertNotNull(match); + assertSame(bob, match.target()); + + match = bindings.match(URI.create("ftp://lmn:21/abc")); + assertNotNull(match); + assertSame(car, match.target()); + } + + @Test + public void requireThatPatternResolutionWorksForFilters() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://*/filtered/*"), foo); + + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + BindingMatch<RequestHandler> match = bindings.match(URI.create("http://localhost:80/status.html")); + assertNull(match); + match = bindings.match(URI.create("http://localhost/filtered/status.html")); + assertNotNull(match); + assertSame(foo, match.target()); + } + + @Test + public void requireThatTreeSplitCanBeBoundForSchemes() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler httpfoo = new NonWorkingRequestHandler(); + RequestHandler httpsfoo = new NonWorkingRequestHandler(); + RequestHandler ftpfoo = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host/foo"), httpfoo); + handlers.put(new UriPattern("https://host/foo"), httpsfoo); + handlers.put(new UriPattern("ftp://host/foo"), ftpfoo); + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + } + + @Test + public void requireThatTreeSplitCanBeBoundForHosts() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + RequestHandler foobar = new NonWorkingRequestHandler(); + RequestHandler fooqux = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://hostabc/foo"), foobar); + handlers.put(new UriPattern("http://hostpqr/foo"), fooqux); + handlers.put(new UriPattern("http://host/foo"), foo); + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + } + + @Test + public void requireThatTreeSplitCanBeBoundForPorts() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo8080 = new NonWorkingRequestHandler(); + RequestHandler foo80 = new NonWorkingRequestHandler(); + RequestHandler foobar = new NonWorkingRequestHandler(); + RequestHandler foopqrbar = new NonWorkingRequestHandler(); + + handlers.put(new UriPattern("http://host:8080/foo"), foo8080); + handlers.put(new UriPattern("http://host:70/foo"), foo80); + handlers.put(new UriPattern("http://hostpqr:70/foo"), foopqrbar); + handlers.put(new UriPattern("http://host:80/foobar"), foobar); + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + } + + @Test + public void requireThatTreeSplitCanBeBoundForPaths() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + RequestHandler foobar = new NonWorkingRequestHandler(); + RequestHandler fooqux = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host/foobar"), foobar); + handlers.put(new UriPattern("http://host/fooqux"), fooqux); + handlers.put(new UriPattern("http://host/foo"), foo); + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + } + + @Test + public void requireThatTreeSplitCanBeBoundForWildcards() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo8080 = new NonWorkingRequestHandler(); + RequestHandler foo80 = new NonWorkingRequestHandler(); + RequestHandler foobar = new NonWorkingRequestHandler(); + RequestHandler foopqrbar = new NonWorkingRequestHandler(); + + handlers.put(new UriPattern("http://host:8080/foo"), foo8080); + handlers.put(new UriPattern("http://host:708/foo"), foo80); + handlers.put(new UriPattern("http://host:80/foobar"), foobar); + handlers.put(new UriPattern("http://hos*:708/foo"), foopqrbar); + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + assertSame(foopqrbar, bindings.resolve(URI.create("http://hostabc:708/foo"))); + assertSame(foo80, bindings.resolve(URI.create("http://host:708/foo"))); + assertSame(foo8080, bindings.resolve(URI.create("http://host:8080/foo"))); + } + + @Test + public void requireThatTreeWorksForURIWithQueryOrFragments() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://*/application/v1/session"), foo); + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + assertSame(foo, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/application/v1/session?name=base"))); + assertSame(foo, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/application/v1/session#application"))); + } + + @Test + public void requireThatTreeWorksForURIWithPathWildCards() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + RequestHandler foo1 = new NonWorkingRequestHandler(); + RequestHandler foo2 = new NonWorkingRequestHandler(); + RequestHandler foo3 = new NonWorkingRequestHandler(); + RequestHandler foo4 = new NonWorkingRequestHandler(); + RequestHandler foo5 = new NonWorkingRequestHandler(); + RequestHandler foo6 = new NonWorkingRequestHandler(); + RequestHandler foo7 = new NonWorkingRequestHandler(); + RequestHandler foo8 = new NonWorkingRequestHandler(); + RequestHandler foo9 = new NonWorkingRequestHandler(); + RequestHandler foo10 = new NonWorkingRequestHandler(); + RequestHandler foo11 = new NonWorkingRequestHandler(); + RequestHandler foo12 = new NonWorkingRequestHandler(); + RequestHandler foo13 = new NonWorkingRequestHandler(); + RequestHandler foo14 = new NonWorkingRequestHandler(); + RequestHandler foo15 = new NonWorkingRequestHandler(); + + handlers.put(new UriPattern("http://*/config/v1/*"), foo); + handlers.put(new UriPattern("http://*/config/v1/*/"), foo1); + handlers.put(new UriPattern("http://*/config/v1/*/*"), foo2); + handlers.put(new UriPattern("http://*/config/v1/*/*/"), foo3); + handlers.put(new UriPattern("http://*/application/v2/tenant/"), foo4); + handlers.put(new UriPattern("http://*/application/v2/tenant/*"), foo5); + handlers.put(new UriPattern("http://*/application/v2/tenant/*/session"), foo6); + handlers.put(new UriPattern("http://*/application/v2/tenant/*/session/*/prepared"), foo7); + handlers.put(new UriPattern("http://*/application/v2/tenant/*/session/*/active"), foo8); + handlers.put(new UriPattern("http://*/application/v2/tenant/*/session/*/content/*"), foo9); + handlers.put(new UriPattern("http://*/application/v2/tenant/*/application/"), foo10); + handlers.put(new UriPattern("http://*/application/v2/tenant/*/application/*/environment/*/" + + "region/*/instance/*/content/*"), foo11); + handlers.put(new UriPattern("http://*/config/v2/tenant/*/application/*/*"), foo12); + handlers.put(new UriPattern("http://*/config/v2/tenant/*/application/*/*/*"), foo13); + handlers.put(new UriPattern("http://*/config/v2/tenant/*/application/*/environment" + + "/*/region/*/instance/*/*"), foo14); + handlers.put(new UriPattern("http://*/config/v2/tenant/*/application/*/*/*/"), foo15); + + + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + assertSame(foo, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/config/v1/cloud.config.log.logd"))); + assertSame(foo1, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/config/v1/cloud.config.log.logd/"))); + assertSame(foo2, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/config/v1/cloud.config.log.logd/admin"))); + assertSame(foo3, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/config/v1/cloud.config.log.logd/admin/"))); + assertSame(foo4, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/application/v2/tenant/"))); + assertSame(foo5, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/application/v2/tenant/b"))); + assertSame(foo6, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/application/v2/tenant/bar/session"))); + assertSame(foo7, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/application/v2/tenant/bar/session/aef/prepared"))); + assertSame(foo8, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/application/v2/tenant/bar/session/a/active"))); + assertSame(foo9, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/application/v2/tenant/bar/session/aef/content/x"))); + assertSame(foo10, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/application/v2/tenant/bar/session/application/"))); + assertSame(foo11, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/application/v2/tenant/bar/application/bbc/environment/xyz/region/m/inst" + + "ance/a/content/l"))); + assertSame(foo12, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/config/v2/tenant/bar/application/bbc/xyz"))); + assertSame(foo13, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/config/v2/tenant/bar/application/bbc/xyz/a"))); + assertSame(foo14, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/config/v2/tenant/bar/application/bbc/environment/a/region/b/instance/a/b"))); + assertSame(foo15, bindings.resolve(URI.create("http://abcxyz.yahoo.com:19071" + + "/config/v2/tenant/bar/application/bbc/xyz/a/c/"))); + } + + @Test + public void requireThatPathOverPortWorks() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler applicationStatus = new NonWorkingRequestHandler(); + RequestHandler search = new NonWorkingRequestHandler(); + RequestHandler legacy = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://*/processing/*"), new NonWorkingRequestHandler()); + handlers.put(new UriPattern("http://*/statistics/*"), new NonWorkingRequestHandler()); + handlers.put(new UriPattern("http://*/state/v1/*"), new NonWorkingRequestHandler()); + handlers.put(new UriPattern("http://*/search/*"), search); + handlers.put(new UriPattern("http://*/status.html"), new NonWorkingRequestHandler()); + handlers.put(new UriPattern("http://*/ApplicationStatus"), applicationStatus); + handlers.put(new UriPattern("http://*:" + getDefaults().vespaWebServicePort() + "/*"), legacy); + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + + assertSame(applicationStatus, bindings.resolve(URI.create + ("http://abcxyz.yahoo.com:" + getDefaults().vespaWebServicePort() + "/ApplicationStatus"))); + assertSame(search, bindings.resolve(URI.create + ("http://abcxyz.yahoo.com:" + getDefaults().vespaWebServicePort() + "/search/?query=sddocname:music"))); + assertSame(legacy, bindings.resolve(URI.create + ("http://abcxyz.yahoo.com:" + getDefaults().vespaWebServicePort() + "/stats/?query=stat:query"))); + } + + @Test + public void requireThatPathOverPortsDoNotWorkOverStricterPatterns() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + RequestHandler bar = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host:4050/a/"), foo); + handlers.put(new UriPattern("http://host/a/"), bar); + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + assertSame(foo, bindings.resolve(URI.create("http://host:4050/a/"))); + } + + @Test + public void requireThatSchemeOrderOverHost() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + RequestHandler bar = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host:5050/a/"), foo); + handlers.put(new UriPattern("ftp://host:5050/a/"), bar); + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + assertSame(foo, bindings.resolve(URI.create("http://host:5050/a/"))); + assertSame(bar, bindings.resolve(URI.create("ftp://host:5050/a/"))); + } + + @Test + public void requireThatPortsAreOrdered() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + RequestHandler bar = new NonWorkingRequestHandler(); + RequestHandler car = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host:5050/a/"), foo); + handlers.put(new UriPattern("http://host:5051/a/"), bar); + handlers.put(new UriPattern("http://host/a/"), car); + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + assertSame(foo, bindings.resolve(URI.create("http://host:5050/a/"))); + assertSame(bar, bindings.resolve(URI.create("http://host:5051/a/"))); + assertSame(car, bindings.resolve(URI.create("http://host/a/"))); + assertSame(car, bindings.resolve(URI.create("http://host:8080/a/"))); + assertSame(car, bindings.resolve(URI.create("http://host:80/a/"))); + } + + @Test + public void requireThatPathsAreOrdered() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + RequestHandler bar = new NonWorkingRequestHandler(); + RequestHandler car = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host:5050/a/"), foo); + handlers.put(new UriPattern("http://host:5050/b/"), bar); + handlers.put(new UriPattern("http://host/a/"), car); + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + assertSame(foo, bindings.resolve(URI.create("http://host:5050/a/"))); + assertSame(bar, bindings.resolve(URI.create("http://host:5050/b/"))); + assertSame(car, bindings.resolve(URI.create("http://host/a/"))); + assertSame(car, bindings.resolve(URI.create("http://host:8080/a/"))); + assertSame(car, bindings.resolve(URI.create("http://host:80/a/"))); + } + + @Test + public void requireThatStrictPatternsOrderBeforeWildcards() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + + RequestHandler fooScheme = new NonWorkingRequestHandler(); + RequestHandler barScheme = new NonWorkingRequestHandler(); + + RequestHandler fooHost = new NonWorkingRequestHandler(); + RequestHandler barHost = new NonWorkingRequestHandler(); + + RequestHandler fooPort = new NonWorkingRequestHandler(); + RequestHandler barPort = new NonWorkingRequestHandler(); + RequestHandler carPort = new NonWorkingRequestHandler(); + + RequestHandler fooPath = new NonWorkingRequestHandler(); + RequestHandler barPath = new NonWorkingRequestHandler(); + + handlers.put(new UriPattern("http://host/x/"), fooScheme); + handlers.put(new UriPattern("*://host/x/"), barScheme); + + handlers.put(new UriPattern("http://host/abc/"), fooHost); + handlers.put(new UriPattern("http://*/abc/"), barHost); + + handlers.put(new UriPattern("http://host:*/a/"), fooPort); + handlers.put(new UriPattern("http://host:5050/b/"), barPort); + handlers.put(new UriPattern("http://host/b/"), carPort); + + handlers.put(new UriPattern("http://hostname/abcde/"), fooPath); + handlers.put(new UriPattern("http://hostname/*/"), barPath); + + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + assertSame(fooScheme, bindings.resolve(URI.create("http://host/x/"))); + assertSame(barScheme, bindings.resolve(URI.create("ftp://host/x/"))); + + assertSame(fooHost, bindings.resolve(URI.create("http://host:8080/abc/"))); + assertSame(barHost, bindings.resolve(URI.create("http://lmn:5050/abc/"))); + + assertSame(fooPort, bindings.resolve(URI.create("http://host:5050/a/"))); + assertSame(barPort, bindings.resolve(URI.create("http://host:5050/b/"))); + assertSame(carPort, bindings.resolve(URI.create("http://host/b/"))); + assertSame(carPort, bindings.resolve(URI.create("http://host:8080/b/"))); + assertSame(carPort, bindings.resolve(URI.create("http://host:80/b/"))); + assertSame(fooPath, bindings.resolve(URI.create("http://hostname/abcde/"))); + assertSame(barPath, bindings.resolve(URI.create("http://hostname/abcd/"))); + + } + + @Test + public void requireThatToStringMethodWorks() { + Map<UriPattern, RequestHandler> handlers = new LinkedHashMap<>(); + RequestHandler foo = new NonWorkingRequestHandler(); + RequestHandler bar = new NonWorkingRequestHandler(); + handlers.put(new UriPattern("http://host/foo"), foo); + handlers.put(new UriPattern("http://host/bar"), bar); + BindingSet<RequestHandler> bindings = new BindingSet<>(handlers.entrySet()); + assertNotNull(bindings); + assertNotNull(bindings.toString()); //Just to get code coverage. + } + + + @Test + public void requireThatPatternsAreOrderedMoreSpecificToLess() { + assertOrder("3://host/path", "2://host/path", "1://host/path"); + assertOrder("http://3/path", "http://2/path", "http://1/path"); + assertOrder("http://host:3/path", "http://host:2/path", "http://host:1/path"); + assertOrder("http://host/3", "http://host/2", "http://host/1"); + assertOrder("http://*/*", "*://host/2", "*://host/1"); + assertOrder("http://host/*", "http://*/2", "http://*/1"); + assertOrder("http://host:*/3", "http://host:2/2", "http://host:1/1"); + assertOrder("http://host/*/3/2/", "http://host/*/1/2", "http://host/*/2/*"); + assertOrder("http://host:69/path", + "http://host/*", + "http://*:69/path", + "http://*/path", + "http://*:69/*", + "http://*/*", + "*://host/path", + "*://*/path", + "*://*/*"); + assertOrder("http://*/HelloWorld", + "http://*:4080/state/v1/*", + "http://*:4083/*", + "http://*:4081/*", + "http://*:4080/*"); + } + + private static void assertOrder(String... expected) { + for (int off = 0; off < expected.length; ++off) { + List<String> actual = new ArrayList<>(); + for (int i = 0; i < expected.length; ++i) { + actual.add(expected[(off + i) % expected.length]); + } + assertOrder(Arrays.asList(expected), actual); + + actual = new ArrayList<>(); + for (int i = expected.length; --i >= 0; ) { + actual.add(expected[(off + i) % expected.length]); + } + assertOrder(Arrays.asList(expected), actual); + } + } + + private static void assertOrder(List<String> expected, List<String> actual) { + BindingRepository<Object> repo = new BindingRepository<>(); + for (String pattern : actual) { + repo.bind(pattern, new Object()); + } + BindingSet<Object> bindings = repo.activate(); + Iterator<Map.Entry<UriPattern, Object>> it = bindings.iterator(); + for (String pattern : expected) { + assertTrue(it.hasNext()); + assertEquals(new UriPattern(pattern), it.next().getKey()); + } + assertFalse(it.hasNext()); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/BundleInstallationExceptionTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BundleInstallationExceptionTestCase.java new file mode 100644 index 00000000000..870db066dc0 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/BundleInstallationExceptionTestCase.java @@ -0,0 +1,53 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import org.junit.Test; +import org.mockito.Mockito; +import org.osgi.framework.Bundle; + +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class BundleInstallationExceptionTestCase { + + @Test + public void requireThatAccessorsWork() { + Throwable t = new Throwable("foo"); + Collection<Bundle> bundles = new LinkedList<>(); + bundles.add(Mockito.mock(Bundle.class)); + BundleInstallationException e = new BundleInstallationException(bundles, t); + assertSame(t, e.getCause()); + assertEquals(t.getMessage(), e.getCause().getMessage()); + assertEquals(bundles, e.installedBundles()); + } + + @Test + public void requireThatBundlesCollectionIsDefensivelyCopied() { + Collection<Bundle> bundles = new LinkedList<>(); + bundles.add(Mockito.mock(Bundle.class)); + BundleInstallationException e = new BundleInstallationException(bundles, new Throwable()); + bundles.add(Mockito.mock(Bundle.class)); + assertEquals(1, e.installedBundles().size()); + } + + @Test + public void requireThatBundlesCollectionIsUnmodifiable() { + BundleInstallationException e = new BundleInstallationException(Arrays.asList(Mockito.mock(Bundle.class)), + new Throwable()); + try { + e.installedBundles().add(Mockito.mock(Bundle.class)); + fail(); + } catch (UnsupportedOperationException f) { + + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerBuilderTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerBuilderTestCase.java new file mode 100644 index 00000000000..811f8fa901b --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerBuilderTestCase.java @@ -0,0 +1,116 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import com.google.inject.AbstractModule; +import com.google.inject.Key; +import com.google.inject.name.Names; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ContainerBuilderTestCase { + + @Test + public void requireThatAccessorsWork() throws URISyntaxException { + final Object obj = new Object(); + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(new AbstractModule() { + + @Override + protected void configure() { + bind(Object.class).toInstance(obj); + bind(String.class).annotatedWith(Names.named("foo")).toInstance("foo"); + } + }); + ContainerBuilder builder = driver.newContainerBuilder(); + assertSame(obj, builder.getInstance(Object.class)); + assertEquals("foo", builder.getInstance(Key.get(String.class, Names.named("foo")))); + + Object ctx = new Object(); + builder.setAppContext(ctx); + assertSame(ctx, builder.appContext()); + + assertTrue(driver.close()); + } + + @Test + public void requireThatContainerThreadFactoryIsBound() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + assertSame(ContainerThread.Factory.class, builder.getInstance(ThreadFactory.class).getClass()); + assertTrue(driver.close()); + } + + @Test + public void requireThatThreadFactoryCanBeReconfigured() { + final ThreadFactory factory = Executors.defaultThreadFactory(); + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.guiceModules().install(new AbstractModule() { + + @Override + protected void configure() { + bind(ThreadFactory.class).toInstance(factory); + } + }); + assertSame(factory, builder.getInstance(ThreadFactory.class)); + assertTrue(driver.close()); + } + + @Test + public void requireThatBindingSetsAreCreatedOnDemand() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + BindingRepository repo = builder.serverBindings("foo"); + assertNotNull(repo); + assertSame(repo, builder.serverBindings("foo")); + assertNotNull(repo = builder.serverBindings("bar")); + assertSame(repo, builder.serverBindings("bar")); + assertNotNull(repo = builder.clientBindings("baz")); + assertSame(repo, builder.clientBindings("baz")); + assertNotNull(repo = builder.clientBindings("cox")); + assertSame(repo, builder.clientBindings("cox")); + driver.close(); + } + + @Test + public void requireThatSafeClassCastWorks() { + ContainerBuilder.safeClassCast(Integer.class, Integer.class); + } + + @Test + public void requireThatSafeClassCastThrowsIllegalArgument() { + try { + ContainerBuilder.safeClassCast(Integer.class, Double.class); + fail(); + } catch (IllegalArgumentException e) { + + } + } + + @Test + public void requireThatSafeStringSplitWorks() { + assertTrue(ContainerBuilder.safeStringSplit(new Object(), ",").isEmpty()); + assertTrue(ContainerBuilder.safeStringSplit("", ",").isEmpty()); + assertTrue(ContainerBuilder.safeStringSplit(" \f\n\r\t", ",").isEmpty()); + assertEquals(Arrays.asList("foo"), ContainerBuilder.safeStringSplit("foo", ",")); + assertEquals(Arrays.asList("foo"), ContainerBuilder.safeStringSplit(" foo", ",")); + assertEquals(Arrays.asList("foo"), ContainerBuilder.safeStringSplit("foo ", ",")); + assertEquals(Arrays.asList("foo"), ContainerBuilder.safeStringSplit("foo, ", ",")); + assertEquals(Arrays.asList("foo"), ContainerBuilder.safeStringSplit("foo ,", ",")); + assertEquals(Arrays.asList("foo", "bar"), ContainerBuilder.safeStringSplit("foo, bar", ",")); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerThreadTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerThreadTestCase.java new file mode 100644 index 00000000000..d92512b3650 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ContainerThreadTestCase.java @@ -0,0 +1,61 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import com.yahoo.jdisc.Metric; +import org.junit.Test; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertSame; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ContainerThreadTestCase { + + @Test + public void requireThatAccessorsWork() { + MetricConsumer consumer = new MyConsumer(); + ContainerThread thread = new ContainerThread(new MyTask(), consumer); + assertSame(consumer, thread.consumer()); + } + + @Test + public void requireThatTaskIsRun() throws InterruptedException { + MyTask task = new MyTask(); + ContainerThread thread = new ContainerThread(task, null); + thread.start(); + task.latch.await(600, TimeUnit.SECONDS); + } + + private static class MyConsumer implements MetricConsumer { + + @Override + public void set(String key, Number val, Metric.Context ctx) { + + } + + @Override + public void add(String key, Number val, Metric.Context ctx) { + + } + + @Override + public Metric.Context createContext(Map<String, ?> properties) { + return null; + } + } + + private static class MyTask implements Runnable { + + final CountDownLatch latch = new CountDownLatch(1); + + @Override + public void run() { + latch.countDown(); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/GlobPatternTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/GlobPatternTestCase.java new file mode 100644 index 00000000000..c9b4650e572 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/GlobPatternTestCase.java @@ -0,0 +1,157 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class GlobPatternTestCase { + + @Test + public void requireThatCompileCreatesExpectedParts() { + assertToString("foo"); + assertToString("*foo"); + assertToString("*oo"); + assertToString("f*o"); + assertToString("fo*"); + assertToString("foo*"); + assertToString("**foo"); + assertToString("**oo"); + assertToString("**o"); + assertToString("f**"); + assertToString("fo**"); + assertToString("foo**"); + assertToString(""); + assertToString("*"); + } + + @Test + public void requireThatGlobMatcherWorks() { + assertMatch("foo", "foo", Collections.<String>emptyList()); + assertNotMatch("foo", "bar"); + + assertMatch("*", "foo", Arrays.asList("foo")); + assertMatch("*", "bar", Arrays.asList("bar")); + + assertMatch("*foo", "foo", Arrays.asList("")); + assertMatch("*oo", "foo", Arrays.asList("f")); + assertMatch("f*o", "foo", Arrays.asList("o")); + assertMatch("fo*", "foo", Arrays.asList("o")); + assertMatch("foo*", "foo", Arrays.asList("")); + + assertNotMatch("*foo", "bar"); + assertNotMatch("*oo", "bar"); + assertNotMatch("f*o", "bar"); + assertNotMatch("fo*", "bar"); + assertNotMatch("foo*", "bar"); + + assertMatch("**foo", "foo", Arrays.asList("", "")); + assertMatch("**oo", "foo", Arrays.asList("", "f")); + assertMatch("f**o", "foo", Arrays.asList("", "o")); + assertMatch("fo**", "foo", Arrays.asList("", "o")); + assertMatch("foo**", "foo", Arrays.asList("", "")); + + assertNotMatch("**foo", "bar"); + assertNotMatch("**oo", "bar"); + assertNotMatch("f**o", "bar"); + assertNotMatch("fo**", "bar"); + assertNotMatch("foo**", "bar"); + + assertMatch("foo bar", "foo bar", Collections.<String>emptyList()); + assertMatch("*foo *bar", "foo bar", Arrays.asList("", "")); + assertMatch("foo* bar*", "foo bar", Arrays.asList("", "")); + assertMatch("f* *r", "foo bar", Arrays.asList("oo", "ba")); + + assertNotMatch("foo bar", "baz cox"); + assertNotMatch("*foo *bar", "baz cox"); + assertNotMatch("foo* bar*", "baz cox"); + assertNotMatch("f* *r", "baz cox"); + } + + @Test + public void requireThatGlobPatternOrdersMoreSpecificFirst() { + assertCompareEq("foo", "foo"); + assertCompareLt("foo", "foo*"); + assertCompareLt("foo", "*foo"); + + assertCompareEq("foo/bar", "foo/bar"); + assertCompareLt("foo/bar", "foo"); + assertCompareLt("foo/bar", "foo*"); + assertCompareLt("foo/bar", "*foo"); + + assertCompareLt("foo/bar", "foo*bar"); + assertCompareLt("foo/bar", "foo*bar*"); + assertCompareLt("foo/bar", "*foo*bar"); + + assertCompareLt("foo*bar", "foo"); + assertCompareLt("foo*bar", "foo*"); + assertCompareLt("foo*bar", "*foo"); + + assertCompareLt("foo", "foo*bar*"); + assertCompareLt("foo*bar*", "foo*"); + assertCompareLt("*foo", "foo*bar*"); + + assertCompareLt("foo", "*foo*bar"); + assertCompareLt("*foo*bar", "foo*"); + assertCompareLt("*foo*bar", "*foo"); + + assertCompareLt("*/3/2", "*/1/2"); + assertCompareLt("*/1/2", "*/2/*"); + } + + @Test + public void requireThatEqualsIsImplemented() { + assertTrue(GlobPattern.compile("foo").equals(GlobPattern.compile("foo"))); + assertFalse(GlobPattern.compile("foo").equals(GlobPattern.compile("bar"))); + } + + @Test + public void requireThatHashCodeIsImplemented() { + assertTrue(GlobPattern.compile("foo").hashCode() == GlobPattern.compile("foo").hashCode()); + assertFalse(GlobPattern.compile("foo").hashCode() == GlobPattern.compile("bar").hashCode()); + } + + private static void assertCompareLt(String lhs, String rhs) { + assertTrue(compare(lhs, rhs) < 0); + assertTrue(compare(rhs, lhs) > 0); + } + + private static void assertCompareEq(String lhs, String rhs) { + assertEquals(0, compare(lhs, rhs)); + assertEquals(0, compare(rhs, lhs)); + } + + private static int compare(String lhs, String rhs) { + return GlobPattern.compile(lhs).compareTo(GlobPattern.compile(rhs)); + } + + private static void assertMatch(String glob, String str, List<String> expected) { + GlobPattern.Match match = GlobPattern.match(glob, str); + assertNotNull(match); + List<String> actual = new ArrayList<>(match.groupCount()); + for (int i = 0, len = match.groupCount(); i < len; ++i) { + actual.add(match.group(i)); + } + assertEquals(expected, actual); + } + + private static void assertNotMatch(String glob, String str) { + assertNull(GlobPattern.match(glob, str)); + } + + private static void assertToString(String pattern) { + assertEquals(pattern, GlobPattern.compile(pattern).toString()); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/GuiceRepositoryTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/GuiceRepositoryTestCase.java new file mode 100644 index 00000000000..39981a812b5 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/GuiceRepositoryTestCase.java @@ -0,0 +1,197 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import com.google.inject.AbstractModule; +import com.google.inject.ConfigurationException; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.PrivateModule; +import com.google.inject.name.Named; +import com.google.inject.name.Names; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class GuiceRepositoryTestCase { + + @Test + public void requireThatInstallWorks() { + GuiceRepository guice = new GuiceRepository(); + StringBinding module = new StringBinding("fooKey", "fooVal"); + guice.install(module); + assertBinding(guice, "fooKey", "fooVal"); + + Iterator<Module> it = guice.iterator(); + assertTrue(it.hasNext()); + assertSame(module, it.next()); + assertFalse(it.hasNext()); + } + + @Test + public void requireThatInstallAllWorks() { + GuiceRepository guice = new GuiceRepository(); + StringBinding foo = new StringBinding("fooKey", "fooVal"); + StringBinding bar = new StringBinding("barKey", "barVal"); + guice.installAll(Arrays.asList(foo, bar)); + assertBinding(guice, "fooKey", "fooVal"); + assertBinding(guice, "barKey", "barVal"); + + Iterator<Module> it = guice.iterator(); + assertTrue(it.hasNext()); + assertSame(foo, it.next()); + assertTrue(it.hasNext()); + assertSame(bar, it.next()); + assertFalse(it.hasNext()); + } + + @Test + public void requireThatUninstallWorks() { + GuiceRepository guice = new GuiceRepository(); + StringBinding module = new StringBinding("fooKey", "fooVal"); + guice.install(module); + assertBinding(guice, "fooKey", "fooVal"); + + guice.uninstall(module); + assertNoBinding(guice, "fooKey"); + assertFalse(guice.iterator().hasNext()); + } + + @Test + public void requireThatUninstallAllWorks() { + GuiceRepository guice = new GuiceRepository(); + StringBinding foo = new StringBinding("fooKey", "fooVal"); + StringBinding bar = new StringBinding("barKey", "barVal"); + StringBinding baz = new StringBinding("bazKey", "bazVal"); + guice.installAll(Arrays.asList(foo, bar, baz)); + assertBinding(guice, "fooKey", "fooVal"); + assertBinding(guice, "barKey", "barVal"); + assertBinding(guice, "bazKey", "bazVal"); + + guice.uninstallAll(Arrays.asList(foo, baz)); + assertNoBinding(guice, "fooKey"); + assertBinding(guice, "barKey", "barVal"); + assertNoBinding(guice, "bazKey"); + + Iterator<Module> it = guice.iterator(); + assertNotNull(it); + assertTrue(it.hasNext()); + assertSame(bar, it.next()); + assertFalse(it.hasNext()); + } + + @Test + public void requireThatBindingsCanBeOverridden() { + GuiceRepository guice = new GuiceRepository(); + guice.install(new StringBinding("fooKey", "fooVal1")); + assertBinding(guice, "fooKey", "fooVal1"); + guice.install(new StringBinding("fooKey", "fooVal2")); + assertBinding(guice, "fooKey", "fooVal2"); + } + + @Test + public void requireThatModulesAreOnlyEvaluatedOnce() { + GuiceRepository guice = new GuiceRepository(); + EvalCounter foo = new EvalCounter(); + EvalCounter bar = new EvalCounter(); + assertEquals(0, foo.cnt); + assertEquals(0, bar.cnt); + guice.install(foo); + assertEquals(1, foo.cnt); + assertEquals(0, bar.cnt); + guice.install(bar); + assertEquals(1, foo.cnt); + assertEquals(1, bar.cnt); + } + + @Test + public void requireThatPrivateModulesWorks() { + GuiceRepository guice = new GuiceRepository(); + + List<Named> names = Arrays.asList(Names.named("A"), Names.named("B")); + + for (Named name: names) { + guice.install(createPrivateInjectNameModule(name)); + } + + Injector injector = guice.getInjector(); + + for (Named name: names) { + NameHolder nameHolder = injector.getInstance(Key.get(NameHolder.class, name)); + assertEquals(name, nameHolder.name); + } + } + + private Module createPrivateInjectNameModule(final Named name) { + return new PrivateModule() { + @Override + protected void configure() { + bind(NameHolder.class).annotatedWith(name).to(NameHolder.class); + expose(NameHolder.class).annotatedWith(name); + bind(Named.class).toInstance(name); + } + }; + } + + private static void assertBinding(GuiceRepository guice, String name, String expected) { + assertEquals(expected, guice.getInjector().getInstance(Key.get(String.class, Names.named(name)))); + } + + private static void assertNoBinding(GuiceRepository guice, String name) { + try { + guice.getInjector().getInstance(Key.get(String.class, Names.named(name))); + fail(); + } catch (ConfigurationException e) { + + } + } + + private static class EvalCounter extends AbstractModule { + + int cnt = 0; + + @Override + protected void configure() { + ++cnt; + } + } + + private static class StringBinding extends AbstractModule { + + final String name; + final String val; + + StringBinding(String name, String val) { + this.name = name; + this.val = val; + } + + @Override + protected void configure() { + bind(String.class).annotatedWith(Names.named(name)).toInstance(val); + } + } + + public static final class NameHolder { + public final Named name; + + @Inject + public NameHolder(Named name) { + this.name = name; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/MetricImplTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/MetricImplTestCase.java new file mode 100644 index 00000000000..84ee425f55f --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/MetricImplTestCase.java @@ -0,0 +1,150 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.yahoo.jdisc.Metric; +import org.junit.Test; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class MetricImplTestCase { + + @Test + public void requireThatClassIsInjectedByDefault() { + Metric metric = Guice.createInjector().getInstance(Metric.class); + assertTrue(metric instanceof MetricImpl); + } + + @Test + public void requireThatConsumerIsOptional() { + Injector injector = Guice.createInjector(); + Metric metric = injector.getInstance(Metric.class); + metric.set("foo", 6, null); + metric.add("foo", 9, null); + } + + @Test + public void requireThatConsumerIsCalled() throws InterruptedException { + final MyConsumer consumer = new MyConsumer(); + Injector injector = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + bind(MetricConsumer.class).toInstance(consumer); + } + }); + Metric metric = injector.getInstance(Metric.class); + metric.set("foo", 6, null); + assertEquals(6, consumer.map.get("foo").intValue()); + metric.add("foo", 9, null); + assertEquals(15, consumer.map.get("foo").intValue()); + Metric.Context ctx = metric.createContext(null); + assertEquals(consumer.ctx, ctx); + } + + @Test + public void requireThatWorkerMetricHasPrecedence() throws InterruptedException { + final MyConsumer globalConsumer = new MyConsumer(); + Injector injector = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + bind(MetricConsumer.class).toInstance(globalConsumer); + } + }); + Metric metric = injector.getInstance(Metric.class); + + MyConsumer localConsumer = new MyConsumer(); + localConsumer.latchRef.set(new CountDownLatch(1)); + new ContainerThread(new SetTask(metric, "foo", 6), localConsumer).start(); + localConsumer.latchRef.get().await(600, TimeUnit.SECONDS); + assertEquals(6, localConsumer.map.get("foo").intValue()); + assertTrue(globalConsumer.map.isEmpty()); + + localConsumer.latchRef.set(new CountDownLatch(1)); + new ContainerThread(new AddTask(metric, "foo", 9), localConsumer).start(); + localConsumer.latchRef.get().await(600, TimeUnit.SECONDS); + assertEquals(15, localConsumer.map.get("foo").intValue()); + assertTrue(globalConsumer.map.isEmpty()); + } + + private static class SetTask implements Runnable { + + final Metric metric; + final String key; + final Number val; + + public SetTask(Metric metric, String key, Number val) { + this.metric = metric; + this.key = key; + this.val = val; + } + + @Override + public void run() { + metric.set(key, val, null); + } + } + + private static class AddTask implements Runnable { + + final Metric metric; + final String key; + final Number val; + + public AddTask(Metric metric, String key, Number val) { + this.metric = metric; + this.key = key; + this.val = val; + } + + @Override + public void run() { + metric.add(key, val, null); + } + } + + private static class MyConsumer implements MetricConsumer { + + final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(); + final AtomicReference<CountDownLatch> latchRef = new AtomicReference<>(); + final Metric.Context ctx = new Metric.Context() { }; + + @Override + public void set(String key, Number val, Metric.Context ctx) { + map.put(key, val.intValue()); + CountDownLatch latch = latchRef.get(); + if (latch != null) { + latch.countDown(); + } + } + + @Override + public void add(String key, Number val, Metric.Context ctx) { + map.put(key, map.get(key) + val.intValue()); + CountDownLatch latch = this.latchRef.get(); + if (latch != null) { + latch.countDown(); + } + } + + @Override + public Metric.Context createContext(Map<String, ?> properties) { + return ctx; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiHeaderTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiHeaderTestCase.java new file mode 100644 index 00000000000..2d167aa08b6 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiHeaderTestCase.java @@ -0,0 +1,20 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class OsgiHeaderTestCase { + + @Test + public void requireThatOsgiHeadersDoNotChange() { + assertEquals("X-JDisc-Application", OsgiHeader.APPLICATION); + assertEquals("X-JDisc-Preinstall-Bundle", OsgiHeader.PREINSTALL_BUNDLE); + assertEquals("X-JDisc-Privileged-Activator", OsgiHeader.PRIVILEGED_ACTIVATOR); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiRepositoryTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiRepositoryTestCase.java new file mode 100644 index 00000000000..2da63190616 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/OsgiRepositoryTestCase.java @@ -0,0 +1,18 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class OsgiRepositoryTestCase { + + @Test + public void requireNothingSinceIntegrationModuleTestsThis() { + assertTrue(true); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ResourcePoolTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ResourcePoolTestCase.java new file mode 100644 index 00000000000..ed113572bb7 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ResourcePoolTestCase.java @@ -0,0 +1,168 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import com.google.inject.AbstractModule; +import com.google.inject.Key; +import com.yahoo.jdisc.AbstractResource; +import com.yahoo.jdisc.ResourceReference; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class ResourcePoolTestCase { + + @Test + public void requireThatAddReturnsArgument() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyResource foo = new MyResource(); + assertSame(foo, new ResourcePool(driver.newContainerBuilder()).add(foo)); + assertTrue(driver.close()); + } + + @Test + public void requireThatAddDoesNotRetainArgument() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyResource foo = new MyResource(); + assertEquals(1, foo.retainCount()); + new ResourcePool(driver.newContainerBuilder()).add(foo); + assertEquals(1, foo.retainCount()); + assertTrue(driver.close()); + } + + @Test + public void requireThatAddCanBeUsedWithoutContainerBuilder() { + new ResourcePool().add(new MyResource()); + } + + @Test + public void requireThatRetainReturnsArgument() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyResource foo = new MyResource(); + assertSame(foo, new ResourcePool(driver.newContainerBuilder()).retain(foo)); + assertTrue(driver.close()); + } + + @Test + public void requireThatRetainRetainsArgument() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyResource foo = new MyResource(); + assertEquals(1, foo.retainCount()); + new ResourcePool(driver.newContainerBuilder()).retain(foo); + assertEquals(2, foo.retainCount()); + assertTrue(driver.close()); + } + + @Test + public void requireThatRetainCanBeUsedWithoutContainerBuilder() { + new ResourcePool().retain(new MyResource()); + } + + @Test + public void requireThatGetReturnsBoundInstance() { + final MyResource foo = new MyResource(); + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(new AbstractModule() { + + @Override + protected void configure() { + bind(MyResource.class).toInstance(foo); + } + }); + ResourcePool pool = new ResourcePool(driver.newContainerBuilder()); + assertSame(foo, pool.get(MyResource.class)); + assertSame(foo, pool.get(Key.get(MyResource.class))); + assertTrue(driver.close()); + } + + @Test + public void requireThatGetDoesNotRetainArgument() { + final MyResource foo = new MyResource(); + assertEquals(1, foo.retainCount()); + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(new AbstractModule() { + + @Override + protected void configure() { + bind(MyResource.class).toInstance(foo); + } + }); + ResourcePool pool = new ResourcePool(driver.newContainerBuilder()); + pool.get(MyResource.class); + assertEquals(1, foo.retainCount()); + pool.get(Key.get(MyResource.class)); + assertEquals(1, foo.retainCount()); + assertTrue(driver.close()); + } + + @Test + public void requireThatGetCanNotBeUsedWithoutContainerBuilder() { + ResourcePool pool = new ResourcePool(); + try { + pool.get(MyResource.class); + fail(); + } catch (NullPointerException e) { + + } + try { + pool.get(Key.get(MyResource.class)); + fail(); + } catch (NullPointerException e) { + + } + } + + @Test + public void requireThatResourcesAreReleasedOnDestroy() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + + ResourcePool pool = new ResourcePool(driver.newContainerBuilder()); + MyResource foo = pool.add(new MyResource()); + MyResource bar = pool.add(new MyResource()); + MyResource baz = pool.add(new MyResource()); + assertEquals(1, pool.retainCount()); + assertEquals(1, foo.retainCount()); + assertEquals(1, bar.retainCount()); + assertEquals(1, baz.retainCount()); + + final ResourceReference secondPoolReference = pool.refer(); + assertEquals(2, pool.retainCount()); + assertEquals(1, foo.retainCount()); + assertEquals(1, bar.retainCount()); + assertEquals(1, baz.retainCount()); + + secondPoolReference.close(); + assertEquals(1, pool.retainCount()); + assertEquals(1, foo.retainCount()); + assertEquals(1, bar.retainCount()); + assertEquals(1, baz.retainCount()); + + pool.release(); + assertEquals(0, pool.retainCount()); + assertEquals(0, foo.retainCount()); + assertEquals(0, bar.retainCount()); + assertEquals(0, baz.retainCount()); + + assertTrue(driver.close()); + } + + @Test + public void requireThatAutoCloseCallsRelease() throws Exception { + MyResource foo = new MyResource(); + assertEquals(1, foo.retainCount()); + try (ResourcePool pool = new ResourcePool()) { + pool.retain(foo); + assertEquals(2, foo.retainCount()); + } + assertEquals(1, foo.retainCount()); + } + + private static class MyResource extends AbstractResource { + + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/ServerRepositoryTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ServerRepositoryTestCase.java new file mode 100644 index 00000000000..6ce125ff590 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/ServerRepositoryTestCase.java @@ -0,0 +1,88 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import com.yahoo.jdisc.NoopSharedResource; +import com.yahoo.jdisc.service.ServerProvider; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Iterator; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ServerRepositoryTestCase { + + @Test + public void requireThatInstallWorks() { + ServerRepository servers = newServerRepository(); + MyServer server = new MyServer(); + servers.install(server); + + Iterator<ServerProvider> it = servers.iterator(); + assertTrue(it.hasNext()); + assertSame(server, it.next()); + assertFalse(it.hasNext()); + } + + @Test + public void requireThatInstallAllWorks() { + ServerRepository servers = newServerRepository(); + ServerProvider foo = new MyServer(); + ServerProvider bar = new MyServer(); + servers.installAll(Arrays.asList(foo, bar)); + + Iterator<ServerProvider> it = servers.iterator(); + assertTrue(it.hasNext()); + assertSame(foo, it.next()); + assertTrue(it.hasNext()); + assertSame(bar, it.next()); + assertFalse(it.hasNext()); + } + + @Test + public void requireThatUninstallWorks() { + ServerRepository servers = newServerRepository(); + ServerProvider server = new MyServer(); + servers.install(server); + servers.uninstall(server); + assertFalse(servers.iterator().hasNext()); + } + + @Test + public void requireThatUninstallAllWorks() { + ServerRepository servers = newServerRepository(); + ServerProvider foo = new MyServer(); + ServerProvider bar = new MyServer(); + ServerProvider baz = new MyServer(); + servers.installAll(Arrays.asList(foo, bar, baz)); + servers.uninstallAll(Arrays.asList(foo, bar)); + Iterator<ServerProvider> it = servers.iterator(); + assertNotNull(it); + assertTrue(it.hasNext()); + assertSame(baz, it.next()); + assertFalse(it.hasNext()); + } + + private static ServerRepository newServerRepository() { + return new ServerRepository(new GuiceRepository()); + } + + private static class MyServer extends NoopSharedResource implements ServerProvider { + + @Override + public void start() { + + } + + @Override + public void close() { + + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/application/UriPatternTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/application/UriPatternTestCase.java new file mode 100644 index 00000000000..c7c45be481a --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/application/UriPatternTestCase.java @@ -0,0 +1,342 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.application; + +import org.junit.Test; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class UriPatternTestCase { + + private static final List<String> NO_GROUPS = Collections.emptyList(); + + @Test + public void requireThatIllegalPatternsAreDetected() { + assertIllegalPattern("scheme"); + assertIllegalPattern("scheme://"); + assertIllegalPattern("scheme://host"); + assertIllegalPattern("scheme://host:0"); + assertIllegalPattern("scheme://host:69"); + assertIllegalPattern("scheme://host:-69"); + assertIllegalPattern("scheme://host:6*/"); + assertIllegalPattern("scheme://host:6*9/"); + assertIllegalPattern("scheme://host:*9/"); + } + + @Test + public void requireThatNoPortImpliesWildcard() { + assertEquals(new UriPattern("scheme://host/path"), + new UriPattern("scheme://host:*/path")); + } + + @Test + public void requireThatPatternMatches() { + // scheme matching + UriPattern pattern = new UriPattern("bar://host:69/path"); + assertNotMatch(pattern, "foobar://host:69/path"); + assertMatch(pattern, "bar://host:69/path", NO_GROUPS); + assertNotMatch(pattern, "barbaz://host:69/path"); + + pattern = new UriPattern("*://host:69/path"); + assertMatch(pattern, "foobar://host:69/path", Arrays.asList("foobar")); + assertMatch(pattern, "bar://host:69/path", Arrays.asList("bar")); + assertMatch(pattern, "barbaz://host:69/path", Arrays.asList("barbaz")); + + pattern = new UriPattern("*bar://host:69/path"); + assertMatch(pattern, "foobar://host:69/path", Arrays.asList("foo")); + assertMatch(pattern, "bar://host:69/path", Arrays.asList("")); + assertNotMatch(pattern, "barbaz://host:69/path"); + + pattern = new UriPattern("bar*://host:69/path"); + assertNotMatch(pattern, "foobar://host:69/path"); + assertMatch(pattern, "bar://host:69/path", Arrays.asList("")); + assertMatch(pattern, "barbaz://host:69/path", Arrays.asList("baz")); + + // host matching + pattern = new UriPattern("scheme://bar:69/path"); + assertNotMatch(pattern, "scheme://foobar:69/path"); + assertMatch(pattern, "scheme://bar:69/path", NO_GROUPS); + assertNotMatch(pattern, "scheme://barbaz:69/path"); + + pattern = new UriPattern("scheme://*:69/path"); + assertMatch(pattern, "scheme://foobar:69/path", Arrays.asList("foobar")); + assertMatch(pattern, "scheme://bar:69/path", Arrays.asList("bar")); + assertMatch(pattern, "scheme://barbaz:69/path", Arrays.asList("barbaz")); + + pattern = new UriPattern("scheme://*bar:69/path"); + assertMatch(pattern, "scheme://foobar:69/path", Arrays.asList("foo")); + assertMatch(pattern, "scheme://bar:69/path", Arrays.asList("")); + assertNotMatch(pattern, "scheme://barbaz:69/path"); + + pattern = new UriPattern("scheme://bar*:69/path"); + assertNotMatch(pattern, "scheme://foobar:69/path"); + assertMatch(pattern, "scheme://bar:69/path", Arrays.asList("")); + assertMatch(pattern, "scheme://barbaz:69/path", Arrays.asList("baz")); + + // port matching + pattern = new UriPattern("scheme://host:69/path"); + assertNotMatch(pattern, "scheme://host:669/path"); + assertMatch(pattern, "scheme://host:69/path", NO_GROUPS); + assertNotMatch(pattern, "scheme://host:699/path"); + + pattern = new UriPattern("scheme://host:*/path"); + assertMatch(pattern, "scheme://host:669/path", Arrays.asList("669")); + assertMatch(pattern, "scheme://host:69/path", Arrays.asList("69")); + assertMatch(pattern, "scheme://host:699/path", Arrays.asList("699")); + + // path matching + pattern = new UriPattern("scheme://host:69/"); + assertMatch(pattern, "scheme://host:69/", NO_GROUPS); + assertNotMatch(pattern, "scheme://host:69/foo"); + + pattern = new UriPattern("scheme://host:69/bar"); + assertNotMatch(pattern, "scheme://host:69/foobar"); + assertMatch(pattern, "scheme://host:69/bar", NO_GROUPS); + assertNotMatch(pattern, "scheme://host:69/barbaz"); + + pattern = new UriPattern("scheme://host:69/*"); + assertMatch(pattern, "scheme://host:69/", Arrays.asList("")); + assertMatch(pattern, "scheme://host:69/foobar", Arrays.asList("foobar")); + assertMatch(pattern, "scheme://host:69/bar", Arrays.asList("bar")); + assertMatch(pattern, "scheme://host:69/barbaz", Arrays.asList("barbaz")); + + pattern = new UriPattern("scheme://host:69/*bar"); + assertMatch(pattern, "scheme://host:69/foobar", Arrays.asList("foo")); + assertMatch(pattern, "scheme://host:69/bar", Arrays.asList("")); + assertNotMatch(pattern, "scheme://host:69/barbaz"); + + pattern = new UriPattern("scheme://host:69/bar*"); + assertNotMatch(pattern, "scheme://host:69/foobar"); + assertMatch(pattern, "scheme://host:69/bar", Arrays.asList("")); + assertMatch(pattern, "scheme://host:69/barbaz", Arrays.asList("baz")); + } + + @Test + public void requireThatUriWithoutHostDoesNotThrowException() { + String schemeOnly = "scheme:schemeSpecificPart"; + String schemeAndPath = "scheme:/path"; + String pathOnly = "path"; + String pathOnlyWithSlash = "/path"; + + UriPattern pattern = new UriPattern("scheme://host/path"); + assertNotMatch(pattern, schemeOnly); + assertNotMatch(pattern, schemeAndPath); + assertNotMatch(pattern, pathOnly); + assertNotMatch(pattern, pathOnlyWithSlash); + + pattern = new UriPattern("scheme*://host*/path*"); + assertNotMatch(pattern, schemeOnly); + assertNotMatch(pattern, schemeAndPath); + assertNotMatch(pattern, pathOnly); + assertNotMatch(pattern, pathOnlyWithSlash); + + pattern = new UriPattern("*://*/*"); + assertMatch(pattern, schemeOnly, Arrays.asList("scheme", "", "")); + assertMatch(pattern, schemeAndPath, Arrays.asList("scheme", "", "path")); + assertMatch(pattern, pathOnly, Arrays.asList("", "", "path")); + assertMatch(pattern, pathOnlyWithSlash, Arrays.asList("", "", "path")); + } + + @Test + public void requireThatUriWithoutPathDoesNotThrowException() { + UriPattern pattern = new UriPattern("scheme://host/path"); + assertNotMatch(pattern, "scheme://host"); + + pattern = new UriPattern("scheme://host/*"); + assertMatch(pattern, "scheme://host", Arrays.asList("")); + } + + @Test + public void requireThatOnlySchemeHostPortAndPathIsMatched() { + UriPattern pattern = new UriPattern("scheme://host:69/path"); + assertMatch(pattern, "scheme://host:69/path?foo", NO_GROUPS); + assertMatch(pattern, "scheme://host:69/path?foo#bar", NO_GROUPS); + } + + @Test + public void requireThatHostSupportsWildcard() { + UriPattern pattern = new UriPattern("scheme://*.host/path"); + assertMatch(pattern, "scheme://a.host/path", Arrays.asList("a")); + assertMatch(pattern, "scheme://a.b.host/path", Arrays.asList("a.b")); + } + + @Test + public void requireThatPrioritiesAreOrderedDescending() { + assertCompareLt(new UriPattern("scheme://host:69/path", 1), + new UriPattern("scheme://host:69/path", 0)); + } + + @Test + public void requireThatPriorityOrdersBeforeScheme() { + assertCompareLt(new UriPattern("*://host:69/path", 1), + new UriPattern("scheme://host:69/path", 0)); + } + + @Test + public void requireThatSchemesAreOrdered() { + assertCompareLt("b://host:69/path", + "a://host:69/path"); + } + + @Test + public void requireThatSchemeOrdersBeforeHost() { + assertCompareLt("b://*:69/path", + "a://host:69/path"); + } + + @Test + public void requireThatHostsAreOrdered() { + assertCompareLt("scheme://b:69/path", + "scheme://a:69/path"); + } + + @Test + public void requireThatHostOrdersBeforePath() { + assertCompareLt("scheme://b:69/*", + "scheme://a:69/path"); + } + + @Test + public void requireThatPortsAreOrdered() { + for (int i = 1; i < 69; ++i) { + assertCompareEq("scheme://host:" + i + "/path", + "scheme://host:" + i + "/path"); + assertCompareLt("scheme://host:" + (i + 1) + "/path", + "scheme://host:" + i + "/path"); + assertCompareLt("scheme://host:" + i + "/path", + "scheme://host:*/path"); + } + } + + @Test + public void requireThatPathsAreOrdered() { + assertCompareLt("scheme://host:69/b", + "scheme://host:69/a"); + } + + @Test + public void requireThatPathOrdersBeforePort() { + assertCompareLt("scheme://host:*/b", + "scheme://host:69/a"); + } + + @Test + public void requireThatEqualPatternsOrderEqual() { + assertCompareEq("scheme://host:69/path", + "scheme://host:69/path"); + assertCompareEq("*://host:69/path", + "*://host:69/path"); + assertCompareEq("scheme://*:69/path", + "scheme://*:69/path"); + assertCompareEq("scheme://host:*/path", + "scheme://host:*/path"); + assertCompareEq("scheme://host:69/*", + "scheme://host:69/*"); + } + + @Test + public void requireThatStrictPatternsOrderBeforeWildcards() { + assertCompareLt("scheme://host:69/path", + "*://host:69/path"); + assertCompareLt("scheme://a:69/path", + "scheme://*:69/path"); + assertCompareLt("scheme://a:69/path", + "scheme://*a:69/path"); + assertCompareLt("scheme://*aa:69/path", + "scheme://*a:69/path"); + assertCompareLt("scheme://host:69/path", + "scheme://host:*/path"); + assertCompareLt("scheme://host:69/a", + "scheme://host:69/*"); + assertCompareLt("scheme://host:69/a", + "scheme://host:69/a*"); + assertCompareLt("scheme://host:69/aa*", + "scheme://host:69/a*"); + assertCompareLt("scheme://*:69/path", + "*://host:69/path"); + assertCompareLt("scheme://host:*/path", + "scheme://*:69/path"); + assertCompareLt("scheme://host:*/path", + "scheme://host:69/*"); + assertCompareLt("scheme://host:69/foo", + "scheme://host:69/*"); + assertCompareLt("scheme://host:69/foo/bar", + "scheme://host:69/foo/*"); + assertCompareLt("scheme://host:69/foo/bar/baz", + "scheme://host:69/foo/bar/*"); + } + + @Test + public void requireThatLongPatternsOrderBeforeShort() { + assertCompareLt("scheme://host:69/foo/bar", + "scheme://host:69/foo"); + assertCompareLt("scheme://host:69/foo/bar/baz", + "scheme://host:69/foo/bar"); + } + + private static void assertIllegalPattern(String uri) { + try { + new UriPattern(uri); + fail(); + } catch (IllegalArgumentException e) { + // expected + } + } + + private static void assertCompareLt(String lhs, String rhs) { + assertCompareLt(new UriPattern(lhs, 0), new UriPattern(rhs, 0)); + } + + private static void assertCompareLt(UriPattern lhs, UriPattern rhs) { + assertEquals(-1, compare(lhs, rhs)); + } + + private static void assertCompareEq(String lhs, String rhs) { + assertCompareEq(new UriPattern(lhs, 0), new UriPattern(rhs, 0)); + } + + private static void assertCompareEq(UriPattern lhs, UriPattern rhs) { + assertEquals(0, compare(lhs, rhs)); + } + + private static int compare(UriPattern lhs, UriPattern rhs) { + int lhsCmp = lhs.compareTo(rhs); + int rhsCmp = rhs.compareTo(lhs); + if (lhsCmp < 0) { + assertTrue(rhsCmp > 0); + return -1; + } + if (lhsCmp > 0) { + assertTrue(rhsCmp < 0); + return 1; + } + assertTrue(rhsCmp == 0); + return 0; + } + + private static void assertMatch(UriPattern pattern, String uri, List<String> expected) { + UriPattern.Match match = pattern.match(URI.create(uri)); + assertNotNull(match); + List<String> actual = new ArrayList<>(match.groupCount()); + for (int i = 0, len = match.groupCount(); i < len; ++i) { + actual.add(match.group(i)); + } + assertEquals(expected, actual); + } + + private static void assertNotMatch(UriPattern pattern, String uri) { + assertNull(pattern.match(URI.create(uri))); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/BindingMatchingTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/BindingMatchingTestCase.java new file mode 100644 index 00000000000..7c858a38b80 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/BindingMatchingTestCase.java @@ -0,0 +1,126 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.benchmark; + +import com.yahoo.jdisc.application.BindingRepository; +import com.yahoo.jdisc.application.BindingSet; +import com.yahoo.jdisc.application.UriPattern; +import org.junit.Test; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class BindingMatchingTestCase { + + private static final int NUM_CANDIDATES = 1024; + private static final int NUM_MATCHES = 100; + private static final int MIN_THREADS = 1; + private static final int MAX_THREADS = 64; + private static final Random random = new Random(); + private static final ExecutorService executor = Executors.newFixedThreadPool(MAX_THREADS); + + @Test + public void runThroughtputMeasurements() throws Exception { + System.err.format("%15s%15s%15s%15s%15s%15s%15s%15s\n", + "No. of Bindings", "1 thread", "2 thread", "4 thread", "8 thread", "16 thread", "32 thread", "64 thread"); + for (int numBindings : Arrays.asList(1, 10, 25, 50, 100, 250)) { + BindingRepository<Object> repo = new BindingRepository<>(); + for (int binding = 0; binding < numBindings; ++binding) { + repo.bind("http://*/v" + binding + "/*/data/", new Object()); + } + System.err.format("%15s", numBindings + " binding(s)"); + + List<URI> candidates = newCandidates(repo); + measureThroughput(repo.activate(), candidates, MAX_THREADS); // warmup + + BindingSet<Object> bindings = repo.activate(); + for (int numThreads = MIN_THREADS; + numThreads <= MAX_THREADS; + numThreads *= 2) + { + System.err.format("%15s", measureThroughput(bindings, candidates, numThreads)); + } + System.err.format("\n"); + } + } + + private static long measureThroughput(BindingSet<Object> bindings, List<URI> candidates, int numThreads) throws Exception { + List<MatchTask> tasks = new LinkedList<>(); + for (int i = 0; i < numThreads; ++i) { + MatchTask task = new MatchTask(bindings, candidates); + tasks.add(task); + } + List<Future<Long>> results = executor.invokeAll(tasks); + long nanos = 0; + for (Future<Long> res : results) { + nanos = Math.max(nanos, res.get()); + } + return (numThreads * NUM_MATCHES * TimeUnit.SECONDS.toNanos(1)) / nanos; + } + + private List<URI> newCandidates(BindingRepository<Object> bindings) { + List<URI> lst = new ArrayList<>(NUM_CANDIDATES); + Iterator<Map.Entry<UriPattern, Object>> it = bindings.iterator(); + for (int i = 0; i < NUM_CANDIDATES; ++i) { + if (!it.hasNext()) { + it = bindings.iterator(); + } + lst.add(newCandidate(it.next().getKey())); + } + return lst; + } + + private URI newCandidate(UriPattern key) { + String pattern = key.toString(); + StringBuilder uri = new StringBuilder(); + for (int i = 0, len = pattern.length(); i < len; ++i) { + char c = pattern.charAt(i); + if (c == '*') { + uri.append(random.nextInt(Integer.MAX_VALUE)); + } else { + uri.append(c); + } + } + return URI.create(uri.toString()); + } + + private static class MatchTask implements Callable<Long> { + + final BindingSet<Object> bindings; + final List<URI> candidates; + + MatchTask(BindingSet<Object> bindings, List<URI> candidates) { + this.bindings = bindings; + this.candidates = candidates; + } + + @Override + public Long call() throws Exception { + Iterator<URI> it = candidates.iterator(); + for (int i = 0, len = random.nextInt(candidates.size()); i < len; ++i) { + it.next(); + } + long time = System.nanoTime(); + for (int i = 0; i < NUM_MATCHES; ++i) { + if (!it.hasNext()) { + it = candidates.iterator(); + } + bindings.match(it.next()); + } + return System.nanoTime() - time; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/LatencyTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/LatencyTestCase.java new file mode 100644 index 00000000000..ec5d1d2f908 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/LatencyTestCase.java @@ -0,0 +1,264 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.benchmark; + +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.handler.AbstractRequestHandler; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.service.CurrentContainer; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class LatencyTestCase { + + private static final int NUM_REQUESTS = 100; + + @Test + public void runLatencyMeasurements() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + MyRequestHandler foo = new MyRequestHandler("foo"); + MyRequestHandler bar = new MyRequestHandler("bar"); + MyRequestHandler baz = new MyRequestHandler("baz"); + builder.serverBindings().bind(foo.uri, foo); + builder.serverBindings().bind(bar.uri, bar); + builder.serverBindings().bind(baz.uri, baz); + driver.activateContainer(builder); + + measureLatencies(NUM_REQUESTS, driver, foo, bar, baz); + TimeTrack time = measureLatencies(NUM_REQUESTS, driver, foo, bar, baz); + System.err.println("\n" + time); + + foo.release(); + bar.release(); + baz.release(); + assertTrue(driver.close()); + } + + private static TimeTrack measureLatencies(int numRequests, CurrentContainer container, + MyRequestHandler... requestHandlers) + { + TimeTrack track = new TimeTrack(); + Random rnd = new Random(); + for (int i = 0; i < numRequests; ++i) { + track.add(measureLatency(container, requestHandlers[rnd.nextInt(requestHandlers.length)])); + } + return track; + } + + private static TimeFrame measureLatency(CurrentContainer container, MyRequestHandler requestHandler) { + TimeFrame frame = new TimeFrame(); + + Request request = null; + ContentChannel requestContent = null; + MyResponseHandler responseHandler = new MyResponseHandler(); + try { + URI uri = URI.create(requestHandler.uri); + request = new Request(container, uri); + frame.handleRequestBegin = System.nanoTime(); + requestContent = request.connect(responseHandler); + frame.handleRequestEnd = requestHandler.handleTime; + } finally { + if (request != null) { + request.release(); + } + } + ByteBuffer buf = ByteBuffer.allocate(69); + MyCompletion requestWrite = new MyCompletion(); + frame.requestWriteBegin = System.nanoTime(); + requestContent.write(buf, requestWrite); + frame.requestWriteEnd = requestHandler.requestContent.writeTime; + frame.requestWriteCompletionBegin = System.nanoTime(); + requestHandler.requestContent.writeCompletion.completed(); + frame.requestWriteCompletionEnd = requestWrite.completedTime; + + MyCompletion requestClose = new MyCompletion(); + frame.requestCloseBegin = System.nanoTime(); + requestContent.close(requestClose); + frame.requestCloseEnd = requestHandler.requestContent.closeTime; + frame.requestCloseCompletionBegin = System.nanoTime(); + requestHandler.requestContent.closeCompletion.completed(); + frame.requestCloseCompletionEnd = requestClose.completedTime; + + Response response = new Response(Response.Status.OK); + frame.handleResponseBegin = System.nanoTime(); + ContentChannel responseContent = requestHandler.responseHandler.handleResponse(response); + frame.handleResponseEnd = responseHandler.handleTime; + MyCompletion responseWrite = new MyCompletion(); + frame.responseWriteBegin = System.nanoTime(); + responseContent.write(buf, responseWrite); + frame.responseWriteEnd = responseHandler.responseContent.writeTime; + frame.responseWriteCompletionBegin = System.nanoTime(); + responseHandler.responseContent.writeCompletion.completed(); + frame.responseWriteCompletionEnd = responseWrite.completedTime; + + MyCompletion responseClose = new MyCompletion(); + frame.responseCloseBegin = System.nanoTime(); + responseContent.close(responseClose); + frame.responseCloseEnd = responseHandler.responseContent.closeTime; + frame.responseCloseCompletionBegin = System.nanoTime(); + responseHandler.responseContent.closeCompletion.completed(); + frame.responseCloseCompletionEnd = responseClose.completedTime; + + return frame; + } + + private static class MyRequestHandler extends AbstractRequestHandler { + + final MyContent requestContent = new MyContent(); + final String uri; + long handleTime; + Request request; + ResponseHandler responseHandler; + + MyRequestHandler(String path) { + this.uri = "http://localhost/" + path; + } + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + handleTime = System.nanoTime(); + this.request = request; + responseHandler = handler; + return requestContent; + } + } + + private static class MyResponseHandler implements ResponseHandler { + + final MyContent responseContent = new MyContent(); + long handleTime; + + @Override + public ContentChannel handleResponse(Response response) { + handleTime = System.nanoTime(); + return responseContent; + } + } + + private static class MyContent implements ContentChannel { + + long writeTime; + long closeTime; + CompletionHandler writeCompletion; + CompletionHandler closeCompletion; + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + writeTime = System.nanoTime(); + writeCompletion = handler; + } + + @Override + public void close(CompletionHandler handler) { + closeTime = System.nanoTime(); + closeCompletion = handler; + } + } + + private static class MyCompletion implements CompletionHandler { + + long completedTime; + + @Override + public void completed() { + completedTime = System.nanoTime(); + } + + @Override + public void failed(Throwable t) { + + } + } + + private static class TimeFrame { + + long handleRequestBegin; + long handleRequestEnd; + long requestWriteBegin; + long requestWriteEnd; + long requestWriteCompletionBegin; + long requestWriteCompletionEnd; + long requestCloseBegin; + long requestCloseEnd; + long requestCloseCompletionBegin; + long requestCloseCompletionEnd; + long handleResponseBegin; + long handleResponseEnd; + long responseWriteBegin; + long responseWriteEnd; + long responseWriteCompletionBegin; + long responseWriteCompletionEnd; + long responseCloseBegin; + long responseCloseEnd; + long responseCloseCompletionBegin; + long responseCloseCompletionEnd; + } + + private static class TimeTrack { + + long frameCnt = 0; + long handleRequest; + long requestWrite; + long requestWriteCompletion; + long requestClose; + long requestCloseCompletion; + long handleResponse; + long responseWrite; + long responseWriteCompletion; + long responseClose; + long responseCloseCompletion; + + public void add(TimeFrame frame) { + ++frameCnt; + handleRequest += frame.handleRequestEnd - frame.handleRequestBegin; + requestWrite += frame.requestWriteEnd - frame.requestWriteBegin; + requestWriteCompletion += frame.requestWriteCompletionEnd - frame.requestWriteCompletionBegin; + requestClose += frame.requestCloseEnd - frame.requestCloseBegin; + requestCloseCompletion += frame.requestCloseCompletionEnd - frame.requestCloseCompletionBegin; + handleResponse += frame.handleResponseEnd - frame.handleResponseBegin; + responseWrite += frame.responseWriteEnd - frame.responseWriteBegin; + responseWriteCompletion += frame.responseWriteCompletionEnd - frame.responseWriteCompletionBegin; + responseClose += frame.responseCloseEnd - frame.responseCloseBegin; + responseCloseCompletion += frame.responseCloseCompletionEnd - frame.responseCloseCompletionBegin; + } + + @Override + public String toString() { + StringBuilder ret = new StringBuilder(); + ret.append("------------------------------------\n"); + ret.append(String.format("HandleRequest : %10.2f\n", (double)handleRequest / frameCnt)); + ret.append(String.format("RequestWrite : %10.2f\n", (double)requestWrite / frameCnt)); + ret.append(String.format("RequestWriteCompletion : %10.2f\n", (double)requestWriteCompletion / frameCnt)); + ret.append(String.format("RequestClose : %10.2f\n", (double)requestClose / frameCnt)); + ret.append(String.format("RequestCloseCompletion : %10.2f\n", (double)requestCloseCompletion / frameCnt)); + ret.append(String.format("HandleResponse : %10.2f\n", (double)handleResponse / frameCnt)); + ret.append(String.format("ResponseWrite : %10.2f\n", (double)responseWrite / frameCnt)); + ret.append(String.format("ResponseWriteCompletion : %10.2f\n", (double)responseWriteCompletion / frameCnt)); + ret.append(String.format("ResponseClose : %10.2f\n", (double)responseClose / frameCnt)); + ret.append(String.format("ResponseCloseCompletion : %10.2f\n", (double)responseCloseCompletion / frameCnt)); + ret.append("------------------------------------\n"); + + double time = (handleRequest + requestWrite + requestWriteCompletion + requestClose + + requestCloseCompletion + handleResponse + responseWrite + responseWriteCompletion + + responseClose + responseCloseCompletion) / frameCnt; + ret.append(String.format("Total nanos : %10.2f\n", time)); + ret.append(String.format("Requests per second : %10.2f\n", TimeUnit.SECONDS.toNanos(1) / time)); + ret.append("------------------------------------\n"); + return ret.toString(); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/ThroughputTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/ThroughputTestCase.java new file mode 100644 index 00000000000..54a94e3e2dd --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/ThroughputTestCase.java @@ -0,0 +1,180 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.benchmark; + +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.handler.AbstractRequestHandler; +import com.yahoo.jdisc.handler.CallableResponseDispatch; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.RequestDispatch; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.handler.ResponseDispatch; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.service.CurrentContainer; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ThroughputTestCase { + + private static final int NUM_REQUESTS = 100; + private static final int MIN_THREADS = 1; + private static final int MAX_THREADS = 64; + private static final int MIN_LOOPS = 0; + private static final int MAX_LOOPS = 1024; + + private static final String HANDLER_URI = "http://localhost/"; + private static final URI REQUEST_URI = URI.create(HANDLER_URI); + private static final ExecutorService executor = Executors.newFixedThreadPool(MAX_THREADS * 2); + private static long preventOptimization = 0; + + @Test + public void runUnthreadedMeasurementsWithWorkload() throws Exception { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + runMeasurements(driver, new UnthreadedHandler(MAX_LOOPS)); // warmup + + StringBuilder out = new StringBuilder(); + out.append("\n"); + out.append(" | "); + for (int i = MIN_THREADS; i <= MAX_THREADS; i *= 2) { + out.append(String.format("%10d", i)); + } + out.append("\n"); + out.append("------+-"); + for (int i = MIN_THREADS; i <= MAX_THREADS; i *= 2) { + out.append("----------"); + } + out.append("\n"); + for (int i = MIN_LOOPS; i <= MAX_LOOPS; i = Math.max(1, i * 2)) { + out.append(String.format("%5d | ", i)); + RequestHandler handler = new UnthreadedHandler(i); + for (Long val : runMeasurements(driver, handler)) { + out.append(String.format("%10d", val)); + } + out.append("\n"); + } + System.err.println(out); + System.err.println(preventOptimization); + assertTrue(driver.close()); + } + + @Test + public void runThreadedMeasurements() throws Exception { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + runMeasurements(driver, new ThreadedHandler()); // warmup + + Iterator<Long> it = runMeasurements(driver, new ThreadedHandler()).iterator(); + for (int numThreads = MIN_THREADS; numThreads <= MAX_THREADS; numThreads *= 2) { + System.err.println(String.format("%2d threads: %10d", numThreads, it.next())); + } + assertTrue(driver.close()); + } + + private static List<Long> runMeasurements(TestDriver driver, RequestHandler handler) throws Exception { + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind(HANDLER_URI, handler); + driver.activateContainer(builder); + handler.release(); + List<Long> ret = new LinkedList<>(); + for (int i = MIN_THREADS; i <= MAX_THREADS; i *= 2) { + ret.add(measureThroughput(driver, i)); + } + return ret; + } + + private static long measureThroughput(CurrentContainer container, int numThreads) throws Exception { + List<RequestTask> tasks = new LinkedList<>(); + for (int i = 0; i < numThreads; ++i) { + RequestTask task = new RequestTask(container); + tasks.add(task); + } + List<Future<Long>> results = executor.invokeAll(tasks); + long nanos = 0; + for (Future<Long> res : results) { + nanos = Math.max(nanos, res.get()); + } + return (numThreads * NUM_REQUESTS * TimeUnit.SECONDS.toNanos(1)) / nanos; + } + + private static class RequestTask implements Callable<Long> { + + final CurrentContainer container; + + RequestTask(CurrentContainer container) { + this.container = container; + } + + @Override + public Long call() throws Exception { + long time = System.nanoTime(); + for (int i = 0; i < NUM_REQUESTS; ++i) { + new RequestDispatch() { + + @Override + protected Request newRequest() { + Request request = new Request(container, REQUEST_URI); + request.setTimeout(600, TimeUnit.SECONDS); + return request; + } + }.dispatch().get(); + } + return System.nanoTime() - time; + } + } + + private static class UnthreadedHandler extends AbstractRequestHandler { + + final int numLoops; + + UnthreadedHandler(int numLoops) { + this.numLoops = numLoops; + } + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + ResponseDispatch.newInstance(Response.Status.OK).dispatch(handler); + preventOptimization += nextLong(); + return null; + } + + long nextLong() { + Random rnd = new Random(); + int k = 0; + for (int i = 0; i < numLoops; ++i) { + k += rnd.nextInt(); + } + return k; + } + } + + private static class ThreadedHandler extends AbstractRequestHandler { + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + executor.submit(new CallableResponseDispatch(handler) { + + @Override + public Response newResponse() { + return new Response(Response.Status.OK); + } + }); + return null; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/UriMatchingTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/UriMatchingTestCase.java new file mode 100644 index 00000000000..df2402d1283 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/benchmark/UriMatchingTestCase.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.jdisc.benchmark; + +import com.yahoo.jdisc.application.UriPattern; +import org.junit.Test; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class UriMatchingTestCase { + + private static final int NUM_MATCHES = 100; + private static long preventOptimization = 0; + + @Test + public void requireThatUriPatternMatchingIsFast() { + List<String> inputs = Arrays.asList( + "other://host/", + "scheme://other/", + "scheme://host/", + "scheme://host/foo", + "scheme://host/foo/bar", + "scheme://host/foo/bar/baz", + "scheme://host/other", + "scheme://host/other/bar", + "scheme://host/other/bar/baz", + "scheme://host/foo/other", + "scheme://host/foo/other/baz", + "scheme://host/foo/bar/other", + "scheme://host:69/", + "scheme://host:69/foo", + "scheme://host:69/foo/bar", + "scheme://host:69/foo/bar/baz", + "scheme://host:96/"); + benchmarkMatch("*://*/*", inputs); // warmup + + runBenchmark("*://*/*", inputs); + runBenchmark("scheme://*/*", inputs); + runBenchmark("scheme://host/*", inputs); + runBenchmark("scheme://host:69/*", inputs); + runBenchmark("scheme://host:69/foo", inputs); + runBenchmark("scheme://host:69/foo/bar", inputs); + runBenchmark("scheme://host:69/foo/bar/baz", inputs); + runBenchmark("*://host:69/foo/bar/baz", inputs); + runBenchmark("*://*/foo/*", inputs); + runBenchmark("*://*/foo/*/baz", inputs); + runBenchmark("*://*/foo/bar/*", inputs); + runBenchmark("*://*/foo/bar/baz", inputs); + runBenchmark("*://*/*/bar", inputs); + runBenchmark("*://*/*/bar/baz", inputs); + runBenchmark("*://*/*/*/baz", inputs); + + System.out.println(">>>>> " + preventOptimization); + } + + private static void runBenchmark(String pattern, List<String> inputs) { + System.out.format("%-30s %10d\n", pattern, benchmarkMatch(pattern, inputs)); + } + + private static long benchmarkMatch(String pattern, List<String> inputs) { + UriPattern compiled = new UriPattern(pattern); + List<URI> uriList = new ArrayList<>(inputs.size()); + for (String input : inputs) { + uriList.add(URI.create(input)); + } + long now = System.nanoTime(); + for (int i = 0; i < NUM_MATCHES; ++i) { + for (URI uri : uriList) { + UriPattern.Match match = compiled.match(uri); + preventOptimization += match != null ? match.groupCount() : 1; + } + } + return TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - now); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/client/AbstractClientApplicationTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/client/AbstractClientApplicationTestCase.java new file mode 100644 index 00000000000..3f04d86c170 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/client/AbstractClientApplicationTestCase.java @@ -0,0 +1,138 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.client; + +import com.google.inject.AbstractModule; +import com.google.inject.Inject; +import com.yahoo.jdisc.application.BundleInstaller; +import com.yahoo.jdisc.application.ContainerActivator; +import com.yahoo.jdisc.service.CurrentContainer; +import org.junit.Test; + +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class AbstractClientApplicationTestCase { + + @Test + public void requireThatApplicationCanBeShutdown() throws Exception { + MyDriver driver = newDriver(); + assertFalse(driver.awaitDone(100, TimeUnit.MILLISECONDS)); + assertTrue(driver.awaitApp(600, TimeUnit.SECONDS)); + driver.app.shutdown(); + assertTrue(driver.app.isShutdown()); + assertTrue(driver.close()); + } + + @Test + public void requireThatShutdownCanBeWaitedForWithTimeout() throws Exception { + final MyDriver driver = newDriver(); + assertFalse(driver.awaitDone(100, TimeUnit.MILLISECONDS)); + assertTrue(driver.awaitApp(600, TimeUnit.SECONDS)); + + final CountDownLatch latch = new CountDownLatch(1); + Executors.newSingleThreadExecutor().submit(new Callable<Boolean>() { + + @Override + public Boolean call() throws Exception { + driver.app.awaitShutdown(600, TimeUnit.SECONDS); + latch.countDown(); + return Boolean.TRUE; + } + }); + assertFalse(latch.await(100, TimeUnit.MILLISECONDS)); + driver.app.shutdown(); + assertTrue(driver.close()); + assertTrue(latch.await(600, TimeUnit.SECONDS)); + } + + @Test + public void requireThatShutdownCanBeWaitedForWithoutTimeout() throws Exception { + final MyDriver driver = newDriver(); + assertFalse(driver.awaitDone(100, TimeUnit.MILLISECONDS)); + assertTrue(driver.awaitApp(600, TimeUnit.SECONDS)); + + final CountDownLatch latch = new CountDownLatch(1); + Executors.newSingleThreadExecutor().submit(new Callable<Boolean>() { + + @Override + public Boolean call() throws Exception { + driver.app.awaitShutdown(); + latch.countDown(); + return Boolean.TRUE; + } + }); + assertFalse(latch.await(100, TimeUnit.MILLISECONDS)); + driver.app.shutdown(); + assertTrue(driver.close()); + assertTrue(latch.await(600, TimeUnit.SECONDS)); + } + + private static MyDriver newDriver() { + final MyDriver driver = new MyDriver(); + driver.done = Executors.newSingleThreadExecutor().submit(new Callable<Boolean>() { + + @Override + public Boolean call() throws Exception { + ClientDriver.runApplication(MyApplication.class, driver); + return Boolean.TRUE; + } + }); + return driver; + } + + private static class MyDriver extends AbstractModule { + + final CountDownLatch appLatch = new CountDownLatch(1); + Future<Boolean> done; + MyApplication app; + + @Override + protected void configure() { + bind(MyDriver.class).toInstance(this); + } + + boolean awaitApp(int timeout, TimeUnit unit) throws InterruptedException { + return appLatch.await(timeout, unit); + } + + boolean awaitDone(int timeout, TimeUnit unit) throws ExecutionException, InterruptedException { + try { + done.get(timeout, unit); + return app.isTerminated(); + } catch (TimeoutException e) { + return false; + } + } + + boolean close() throws ExecutionException, InterruptedException { + return awaitDone(600, TimeUnit.SECONDS); + } + } + + private static class MyApplication extends AbstractClientApplication { + + @Inject + MyApplication(BundleInstaller bundleInstaller, ContainerActivator activator, + CurrentContainer container, MyDriver driver) { + super(bundleInstaller, activator, container); + driver.app = this; + driver.appLatch.countDown(); + } + + @Override + public void start() { + + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/client/ClientDriverTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/client/ClientDriverTestCase.java new file mode 100644 index 00000000000..bef78a22de7 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/client/ClientDriverTestCase.java @@ -0,0 +1,78 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.client; + +import com.google.inject.AbstractModule; +import com.google.inject.Inject; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ClientDriverTestCase { + + @Test + public void requireThatApplicationInstanceInjectionWorks() throws Exception { + MyModule module = new MyModule(); + ClientDriver.runApplication(new MyApplication(module)); + assertEquals(5, module.state); + } + + @Test + public void requireThatApplicationClassInjectionWorks() throws Exception { + MyModule module = new MyModule(); + ClientDriver.runApplication(MyApplication.class, module); + assertEquals(5, module.state); + } + + private static class MyApplication implements ClientApplication { + + final MyModule module; + + @Inject + MyApplication(MyModule module) { + this.module = module; + module.state = 1; + } + + @Override + public void start() { + if (++module.state != 2) { + throw new IllegalStateException(); + } + } + + @Override + public void run() { + if (++module.state != 3) { + throw new IllegalStateException(); + } + } + + @Override + public void stop() { + if (++module.state != 4) { + throw new IllegalStateException(); + } + } + + @Override + public void destroy() { + if (++module.state != 5) { + throw new IllegalStateException(); + } + } + } + + private static class MyModule extends AbstractModule { + + int state = 0; + + @Override + protected void configure() { + bind(MyModule.class).toInstance(this); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ActiveContainerFinalizerTest.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ActiveContainerFinalizerTest.java new file mode 100644 index 00000000000..b2fd357b30c --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ActiveContainerFinalizerTest.java @@ -0,0 +1,75 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.yahoo.jdisc.Container; +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.test.TestDriver; + +import java.net.URI; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +@SuppressWarnings("UnusedAssignment") +public class ActiveContainerFinalizerTest { + + @Test + public void requireThatMissingContainerReleaseDoesNotPreventShutdown() throws InterruptedException { + final TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + driver.activateContainer(driver.newContainerBuilder()); + Container container = driver.newReference(URI.create("scheme://host")); + assertNotNull(container); + + final Termination termination = new Termination(); + driver.activateContainer(null).notifyTermination(termination); + assertFalse(termination.await(100, TimeUnit.MILLISECONDS)); + + container = null; // intentionally doing this instead of container.release() + assertTrue(termination.await(600, TimeUnit.SECONDS)); + assertTrue(driver.close()); + } + + @Test + public void requireThatMissingRequestReleaseDoesNotPreventShutdown() throws InterruptedException { + final TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + driver.activateContainer(driver.newContainerBuilder()); + Request request = new Request(driver, URI.create("scheme://host")); + assertNotNull(request); + + final Termination termination = new Termination(); + driver.activateContainer(null).notifyTermination(termination); + assertFalse(termination.await(100, TimeUnit.MILLISECONDS)); + + request = null; // intentionally doing this instead of request.release() + assertTrue(termination.await(600, TimeUnit.SECONDS)); + assertTrue(driver.close()); + } + + private static class Termination implements Runnable { + + volatile boolean done; + + @Override + public void run() { + done = true; + } + + boolean await(final int timeout, final TimeUnit unit) throws InterruptedException { + final long timeoutAt = System.currentTimeMillis() + unit.toMillis(timeout); + while (!done) { + if (System.currentTimeMillis() > timeoutAt) { + return false; + } + Thread.sleep(10); + System.gc(); + } + return true; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ActiveContainerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ActiveContainerTestCase.java new file mode 100644 index 00000000000..5d61e55b7b4 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ActiveContainerTestCase.java @@ -0,0 +1,160 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.google.inject.AbstractModule; +import com.yahoo.jdisc.application.BindingSet; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.application.UriPattern; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.service.ServerProvider; +import com.yahoo.jdisc.test.NonWorkingRequestHandler; +import com.yahoo.jdisc.test.NonWorkingServerProvider; +import com.yahoo.jdisc.test.TestDriver; + +import java.util.Iterator; +import java.util.Map; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ActiveContainerTestCase { + + @Test + public void requireThatGuiceAccessorWorks() { + final Object obj = new Object(); + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(new AbstractModule() { + + @Override + protected void configure() { + bind(Object.class).toInstance(obj); + } + }); + ActiveContainer container = new ActiveContainer(driver.newContainerBuilder()); + assertSame(obj, container.guiceInjector().getInstance(Object.class)); + driver.close(); + } + + @Test + public void requireThatServerAccessorWorks() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + ServerProvider foo = new NonWorkingServerProvider(); + builder.serverProviders().install(foo); + ServerProvider bar = new NonWorkingServerProvider(); + builder.serverProviders().install(bar); + ActiveContainer container = new ActiveContainer(builder); + + Iterator<ServerProvider> it = container.serverProviders().iterator(); + assertTrue(it.hasNext()); + assertSame(foo, it.next()); + assertTrue(it.hasNext()); + assertSame(bar, it.next()); + assertFalse(it.hasNext()); + driver.close(); + } + + @Test + public void requireThatServerBindingAccessorWorks() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + RequestHandler foo = new NonWorkingRequestHandler(); + RequestHandler bar = new NonWorkingRequestHandler(); + builder.serverBindings().bind("http://host/foo", foo); + builder.serverBindings("bar").bind("http://host/bar", bar); + ActiveContainer container = new ActiveContainer(builder); + + Map<String, BindingSet<RequestHandler>> bindings = container.serverBindings(); + assertNotNull(bindings); + assertEquals(2, bindings.size()); + + BindingSet<RequestHandler> set = bindings.get(BindingSet.DEFAULT); + assertNotNull(set); + Iterator<Map.Entry<UriPattern, RequestHandler>> it = set.iterator(); + assertNotNull(it); + assertTrue(it.hasNext()); + Map.Entry<UriPattern, RequestHandler> entry = it.next(); + assertNotNull(entry); + assertEquals(new UriPattern("http://host/foo"), entry.getKey()); + assertSame(foo, entry.getValue()); + assertFalse(it.hasNext()); + + assertNotNull(set = bindings.get("bar")); + assertNotNull(it = set.iterator()); + assertTrue(it.hasNext()); + assertNotNull(entry = it.next()); + assertEquals(new UriPattern("http://host/bar"), entry.getKey()); + assertSame(bar, entry.getValue()); + assertFalse(it.hasNext()); + + assertNotNull(bindings = container.clientBindings()); + assertEquals(1, bindings.size()); + assertNotNull(set = bindings.get(BindingSet.DEFAULT)); + assertNotNull(it = set.iterator()); + assertFalse(it.hasNext()); + + driver.close(); + } + + @Test + public void requireThatClientBindingAccessorWorks() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + RequestHandler foo = new NonWorkingRequestHandler(); + RequestHandler bar = new NonWorkingRequestHandler(); + builder.clientBindings().bind("http://host/foo", foo); + builder.clientBindings("bar").bind("http://host/bar", bar); + ActiveContainer container = new ActiveContainer(builder); + + Map<String, BindingSet<RequestHandler>> bindings = container.clientBindings(); + assertNotNull(bindings); + assertEquals(2, bindings.size()); + + BindingSet<RequestHandler> set = bindings.get(BindingSet.DEFAULT); + assertNotNull(set); + Iterator<Map.Entry<UriPattern, RequestHandler>> it = set.iterator(); + assertNotNull(it); + assertTrue(it.hasNext()); + Map.Entry<UriPattern, RequestHandler> entry = it.next(); + assertNotNull(entry); + assertEquals(new UriPattern("http://host/foo"), entry.getKey()); + assertSame(foo, entry.getValue()); + assertFalse(it.hasNext()); + + assertNotNull(set = bindings.get("bar")); + assertNotNull(it = set.iterator()); + assertTrue(it.hasNext()); + assertNotNull(entry = it.next()); + assertEquals(new UriPattern("http://host/bar"), entry.getKey()); + assertSame(bar, entry.getValue()); + assertFalse(it.hasNext()); + + assertNotNull(bindings = container.serverBindings()); + assertEquals(1, bindings.size()); + assertNotNull(set = bindings.get(BindingSet.DEFAULT)); + assertNotNull(it = set.iterator()); + assertFalse(it.hasNext()); + + driver.close(); + } + + @Test + public void requireThatDefaultBindingsAreAlwaysCreated() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + ActiveContainer container = new ActiveContainer(builder); + + Map<String, BindingSet<RequestHandler>> bindings = container.serverBindings(); + assertNotNull(bindings); + assertEquals(1, bindings.size()); + BindingSet<RequestHandler> set = bindings.get(BindingSet.DEFAULT); + assertFalse(set.iterator().hasNext()); + driver.close(); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationConfigModuleTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationConfigModuleTestCase.java new file mode 100644 index 00000000000..1d23d671e0f --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationConfigModuleTestCase.java @@ -0,0 +1,114 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.name.Names; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.junit.Test; +import static org.junit.Assert.fail; +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class ApplicationConfigModuleTestCase { + + @Test + public void requireThatEntriesAreBoundWithLowerCaseKeys() { + Map<String, String> config = new HashMap<>(); + config.put("foo_key", "foo"); + config.put("BAR_key", "bar"); + config.put("BAZ_KEY", "baz"); + + Injector injector = Guice.createInjector(new ApplicationConfigModule(config)); + assertBinding(injector, "foo_key", "foo"); + assertBinding(injector, "bar_key", "bar"); + assertBinding(injector, "baz_key", "baz"); + } + + @Test + public void requireThatEntriesAreBoundWithUnmodifiedValue() { + Map<String, String> config = new HashMap<>(); + config.put("foo", "foo_val"); + config.put("bar", "BAR_val"); + config.put("baz", "BAZ_VAL"); + + Injector injector = Guice.createInjector(new ApplicationConfigModule(config)); + assertBinding(injector, "foo", "foo_val"); + assertBinding(injector, "bar", "BAR_val"); + assertBinding(injector, "baz", "BAZ_VAL"); + } + + @Test + public void requireThatUpperCaseKeysPrecedeLowerCaseKeys() { + Map<String, String> config = new HashMap<>(); + config.put("foo", "lower-case"); + assertBinding(config, "foo", "lower-case"); + + config.put("Foo", "mixed-case 1"); + assertBinding(config, "foo", "mixed-case 1"); + + config.put("FOO", "upper-case"); + assertBinding(config, "foo", "upper-case"); + + config.put("FOo", "mixed-case 2"); + assertBinding(config, "foo", "upper-case"); + } + + @Test + public void requireThatNullFileNameThrowsException() throws IOException { + try { + ApplicationConfigModule.newInstanceFromFile(null); + fail(); + } catch (NullPointerException e) { + + } + } + + @Test + public void requireThatFileNotFoundThrowsException() throws IOException { + try { + ApplicationConfigModule.newInstanceFromFile("/file/not/found"); + fail(); + } catch (FileNotFoundException e) { + + } + } + + @Test + public void requireThatPropertieFilesCanBeRead() throws IOException { + Properties props = new Properties(); + props.put("foo_key", "foo_val"); + + File file = File.createTempFile("config-", ".properties"); + file.deleteOnExit(); + FileOutputStream out = new FileOutputStream(file); + props.store(out, null); + out.close(); + + assertBinding(ApplicationConfigModule.newInstanceFromFile(file.getAbsolutePath()), "foo_key", "foo_val"); + } + + private static void assertBinding(Map<String, String> config, String stringName, String expected) { + assertBinding(new ApplicationConfigModule(config), stringName, expected); + } + + private static void assertBinding(Module module, String stringName, String expected) { + assertBinding(Guice.createInjector(module), stringName, expected); + } + + private static void assertBinding(Injector injector, String stringName, String expected) { + assertEquals(expected, injector.getInstance(Key.get(String.class, Names.named(stringName)))); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationEnvironmentModuleTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationEnvironmentModuleTestCase.java new file mode 100644 index 00000000000..77af705cfac --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationEnvironmentModuleTestCase.java @@ -0,0 +1,57 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.google.inject.*; +import com.yahoo.jdisc.application.ContainerActivator; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.application.OsgiFramework; +import com.yahoo.jdisc.service.CurrentContainer; +import com.yahoo.jdisc.test.NonWorkingOsgiFramework; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ThreadFactory; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotNull; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ApplicationEnvironmentModuleTestCase { + + @Test + public void requireThatBindingsExist() { + List<Class> expected = new LinkedList<>(); + expected.add(ContainerActivator.class); + expected.add(ContainerBuilder.class); + expected.add(CurrentContainer.class); + expected.add(OsgiFramework.class); + expected.add(ThreadFactory.class); + + Injector injector = Guice.createInjector(); + for (Map.Entry<Key<?>, Binding<?>> entry : injector.getBindings().entrySet()) { + expected.add(entry.getKey().getTypeLiteral().getRawType()); + } + + ApplicationLoader loader = new ApplicationLoader(new NonWorkingOsgiFramework(), + Collections.<Module>emptyList()); + injector = Guice.createInjector(new ApplicationEnvironmentModule(loader)); + for (Map.Entry<Key<?>, Binding<?>> entry : injector.getBindings().entrySet()) { + assertNotNull(expected.remove(entry.getKey().getTypeLiteral().getRawType())); + } + assertTrue(expected.isEmpty()); + } + + @Test + public void requireThatContainerBuilderCanBeInjected() { + ApplicationLoader loader = new ApplicationLoader(new NonWorkingOsgiFramework(), + Collections.<Module>emptyList()); + assertNotNull(new ApplicationEnvironmentModule(loader).containerBuilder()); + assertNotNull(Guice.createInjector(new ApplicationEnvironmentModule(loader)) + .getInstance(ContainerBuilder.class)); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationLoaderTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationLoaderTestCase.java new file mode 100644 index 00000000000..398fbcba839 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationLoaderTestCase.java @@ -0,0 +1,259 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.google.inject.AbstractModule; +import com.google.inject.ConfigurationException; +import com.google.inject.Module; +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.application.Application; +import com.yahoo.jdisc.application.ApplicationNotReadyException; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.handler.AbstractRequestHandler; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.service.CurrentContainer; +import com.yahoo.jdisc.test.NonWorkingOsgiFramework; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; +import org.osgi.framework.BundleContext; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ApplicationLoaderTestCase { + + @Test + public void requireThatStartFailsWithoutApplication() throws Exception { + ApplicationLoader loader = new ApplicationLoader(new NonWorkingOsgiFramework(), + Collections.<Module>emptyList()); + try { + loader.init(null, false); + loader.start(); + fail(); + } catch (ConfigurationException e) { + + } + } + + @Test + public void requireThatStopDoesNotFailWithoutStart() throws Exception { + ApplicationLoader loader = new ApplicationLoader(new NonWorkingOsgiFramework(), + Collections.<Module>emptyList()); + loader.stop(); + loader.destroy(); + } + + @Test + public void requireThatDestroyDoesNotFailWithActiveContainer() throws Exception { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + assertNull(driver.activateContainer(driver.newContainerBuilder())); + assertTrue(driver.close()); + } + + @Test + public void requireThatApplicationStartExceptionUnsetsAndDestroysApplication() throws Exception { + MyApplication app = MyApplication.newStartException(); + ApplicationLoader loader = new ApplicationLoader(new NonWorkingOsgiFramework(), + Arrays.asList(new MyApplicationModule(app))); + loader.init(null, false); + try { + loader.start(); + fail(); + } catch (MyException e) { + + } + assertNull(loader.application()); + assertFalse(app.stop.await(100, TimeUnit.MILLISECONDS)); + assertTrue(app.destroy.await(600, TimeUnit.SECONDS)); + try { + loader.activateContainer(loader.newContainerBuilder()); + fail(); + } catch (ApplicationNotReadyException e) { + + } + loader.stop(); + loader.destroy(); + } + + @Test + public void requireThatApplicationStopExceptionDestroysApplication() throws Exception { + MyApplication app = MyApplication.newStopException(); + ApplicationLoader loader = new ApplicationLoader(new NonWorkingOsgiFramework(), + Arrays.asList(new MyApplicationModule(app))); + loader.init(null, false); + loader.start(); + try { + loader.stop(); + } catch (MyException e) { + + } + assertTrue(app.destroy.await(600, TimeUnit.SECONDS)); + loader.destroy(); + } + + @Test + public void requireThatApplicationDestroyIsCalledAfterContainerTermination() throws InterruptedException { + MyApplication app = MyApplication.newInstance(); + TestDriver driver = TestDriver.newInjectedApplicationInstance(app); + ContainerBuilder builder = driver.newContainerBuilder(); + MyRequestHandler requestHandler = new MyRequestHandler(); + builder.serverBindings().bind("scheme://host/path", requestHandler); + driver.activateContainer(builder); + driver.dispatchRequest("scheme://host/path", new MyResponseHandler()); + driver.scheduleClose(); + assertFalse(app.destroy.await(100, TimeUnit.MILLISECONDS)); + requestHandler.responseHandler.handleResponse(new Response(Response.Status.OK)).close(null); + assertTrue(app.destroy.await(600, TimeUnit.SECONDS)); + } + + @Test + public void requireThatContainerActivatorReturnsPrev() throws Exception { + TestDriver driver = TestDriver.newInjectedApplicationInstance(MyApplication.newInstance()); + assertNull(driver.activateContainer(driver.newContainerBuilder())); + assertNotNull(driver.activateContainer(null)); + assertTrue(driver.close()); + } + + @Test + public void requireThatOsgiServicesAreRegistered() { + TestDriver driver = TestDriver.newSimpleApplicationInstance(); + BundleContext ctx = driver.osgiFramework().bundleContext(); + Object service = ctx.getService(ctx.getServiceReference(CurrentContainer.class.getName())); + assertTrue(service instanceof CurrentContainer); + assertTrue(driver.close()); + } + + @Test + public void requireThatThreadFactoryCanBeBound() { + final ThreadFactory factory = Executors.defaultThreadFactory(); + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(new AbstractModule() { + + @Override + protected void configure() { + bind(ThreadFactory.class).toInstance(factory); + } + }); + ContainerBuilder builder = driver.newContainerBuilder(); + assertSame(factory, builder.getInstance(ThreadFactory.class)); + assertTrue(driver.close()); + } + + private static class MyApplicationModule extends AbstractModule { + + final Application application; + + public MyApplicationModule(Application application) { + this.application = application; + } + + @Override + protected void configure() { + bind(Application.class).toInstance(application); + } + } + + private static class MyApplication implements Application { + + final CountDownLatch start = new CountDownLatch(1); + final CountDownLatch stop = new CountDownLatch(1); + final CountDownLatch destroy = new CountDownLatch(1); + final boolean startException; + final boolean stopException; + + MyApplication(boolean startException, boolean stopException) { + this.startException = startException; + this.stopException = stopException; + } + + @Override + public void start() { + start.countDown(); + if (startException) { + throw new MyException(); + } + } + + @Override + public void stop() { + stop.countDown(); + if (stopException) { + throw new MyException(); + } + } + + @Override + public void destroy() { + destroy.countDown(); + } + + public static MyApplication newInstance() { + return new MyApplication(false, false); + } + + public static MyApplication newStartException() { + return new MyApplication(true, false); + } + + public static MyApplication newStopException() { + return new MyApplication(false, true); + } + } + + private static class MyRequestHandler extends AbstractRequestHandler { + + ResponseHandler responseHandler; + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + responseHandler = handler; + return new MyContentChannel(); + } + } + + private static class MyResponseHandler implements ResponseHandler { + + @Override + public ContentChannel handleResponse(Response response) { + return new MyContentChannel(); + } + } + + private static class MyContentChannel implements ContentChannel { + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + if (handler != null) { + handler.completed(); + } + } + + @Override + public void close(CompletionHandler handler) { + if (handler != null) { + handler.completed(); + } + } + } + + private static class MyException extends RuntimeException { + + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationRestartTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationRestartTestCase.java new file mode 100644 index 00000000000..2943e44bc4c --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationRestartTestCase.java @@ -0,0 +1,153 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.google.inject.AbstractModule; +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.application.Application; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.handler.AbstractRequestHandler; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.test.NonWorkingOsgiFramework; +import org.junit.Test; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ApplicationRestartTestCase { + + @Test + public void requireThatStopStartDoesNotBreakShutdown() throws Exception { + ApplicationLoader loader = newApplicationLoader(); + loader.init(null, false); + loader.start(); + assertGracefulStop(loader); + loader.start(); + assertGracefulStop(loader); + loader.destroy(); + } + + @Test + public void requireThatDestroyInitDoesNotBreakShutdown() throws Exception { + ApplicationLoader loader = newApplicationLoader(); + loader.init(null, false); + loader.start(); + assertGracefulStop(loader); + loader.destroy(); + loader.init(null, false); + loader.start(); + assertGracefulStop(loader); + loader.destroy(); + } + + private static ApplicationLoader newApplicationLoader() { + return new ApplicationLoader(new NonWorkingOsgiFramework(), + Arrays.asList(new AbstractModule() { + @Override + public void configure() { + bind(Application.class).to(SimpleApplication.class); + } + })); + } + + private static void assertGracefulStop(ApplicationLoader loader) throws Exception { + MyRequestHandler requestHandler = new MyRequestHandler(); + ContainerBuilder builder = loader.newContainerBuilder(); + builder.serverBindings().bind("http://host/path", requestHandler); + loader.activateContainer(builder); + + MyResponseHandler responseHandler = new MyResponseHandler(); + Request request = new Request(loader, URI.create("http://host/path")); + request.connect(responseHandler).close(null); + request.release(); + + StopTask task = new StopTask(loader); + task.start(); + assertFalse(task.latch.await(100, TimeUnit.MILLISECONDS)); + requestHandler.responseHandler.handleResponse(new Response(Response.Status.OK)).close(null); + assertTrue(task.latch.await(600, TimeUnit.SECONDS)); + } + + private static class StopTask extends Thread { + + final ApplicationLoader loader; + final CountDownLatch latch = new CountDownLatch(1); + + StopTask(ApplicationLoader loader) { + this.loader = loader; + } + + @Override + public void run() { + try { + loader.stop(); + } catch (Exception e) { + e.printStackTrace(); + return; + } + latch.countDown(); + } + } + + private static class SimpleApplication implements Application { + + @Override + public void start() { + + } + + @Override + public void stop() { + + } + + @Override + public void destroy() { + + } + } + + private static class MyRequestHandler extends AbstractRequestHandler { + + ResponseHandler responseHandler; + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + this.responseHandler = handler; + return new MyContentChannel(); + } + } + + private static class MyResponseHandler implements ResponseHandler { + + @Override + public ContentChannel handleResponse(Response response) { + return new MyContentChannel(); + } + } + + private static class MyContentChannel implements ContentChannel { + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + handler.completed(); + } + + @Override + public void close(CompletionHandler handler) { + handler.completed(); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationShutdownTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationShutdownTestCase.java new file mode 100644 index 00000000000..986ceddb3e4 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ApplicationShutdownTestCase.java @@ -0,0 +1,122 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.handler.AbstractRequestHandler; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ApplicationShutdownTestCase { + + @Test + public void requireThatStopWaitsForPreviousContainer() throws Exception { + Context ctx = new Context(); + MyRequestHandler requestHandler = new MyRequestHandler(); + ctx.activateContainer(requestHandler); + ctx.dispatchRequest(); + ctx.activateContainer(null); + ctx.driver.scheduleClose(); + assertFalse(ctx.driver.awaitClose(100, TimeUnit.MILLISECONDS)); + requestHandler.respond(); + assertTrue(ctx.driver.awaitClose(600, TimeUnit.SECONDS)); + } + + @Test + public void requireThatStopWaitsForAllPreviousContainers() { + Context ctx = new Context(); + MyRequestHandler requestHandlerA = new MyRequestHandler(); + ctx.activateContainer(requestHandlerA); + ctx.dispatchRequest(); + + MyRequestHandler requestHandlerB = new MyRequestHandler(); + ctx.activateContainer(requestHandlerB); + ctx.dispatchRequest(); + + MyRequestHandler requestHandlerC = new MyRequestHandler(); + ctx.activateContainer(requestHandlerC); + ctx.dispatchRequest(); + + ctx.driver.scheduleClose(); + assertFalse(ctx.driver.awaitClose(100, TimeUnit.MILLISECONDS)); + requestHandlerB.respond(); + assertFalse(ctx.driver.awaitClose(100, TimeUnit.MILLISECONDS)); + requestHandlerC.respond(); + assertFalse(ctx.driver.awaitClose(100, TimeUnit.MILLISECONDS)); + requestHandlerA.respond(); + assertTrue(ctx.driver.awaitClose(600, TimeUnit.SECONDS)); + } + + private static class Context { + + final TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + + void activateContainer(RequestHandler requestHandler) { + ContainerBuilder builder; + if (requestHandler != null) { + builder = driver.newContainerBuilder(); + builder.serverBindings().bind("http://host/path", requestHandler); + } else { + builder = null; + } + driver.activateContainer(builder); + } + + void dispatchRequest() { + Request request = new Request(driver, URI.create("http://host/path")); + request.connect(new MyResponseHandler()).close(null); + request.release(); + } + } + + private static class MyRequestHandler extends AbstractRequestHandler { + + ResponseHandler handler = null; + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + this.handler = handler; + return new MyContent(); + } + + void respond() { + handler.handleResponse(new Response(Response.Status.OK)).close(null); + } + } + + private static class MyResponseHandler implements ResponseHandler { + + @Override + public ContentChannel handleResponse(Response response) { + return new MyContent(); + } + } + + private static class MyContent implements ContentChannel { + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + handler.completed(); + } + + @Override + public void close(CompletionHandler handler) { + handler.completed(); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/BootstrapDaemonTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/BootstrapDaemonTestCase.java new file mode 100644 index 00000000000..bc80358b760 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/BootstrapDaemonTestCase.java @@ -0,0 +1,154 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import org.apache.commons.daemon.DaemonContext; +import org.apache.commons.daemon.DaemonController; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class BootstrapDaemonTestCase { + + @Test + public void requireThatPrivilegedLifecycleWorks() throws Exception { + MyLoader loader = new MyLoader(); + BootstrapDaemon daemon = new BootstrapDaemon(loader, true); + daemon.init(new MyContext("foo")); + assertTrue(loader.hasState(true, false, false, false)); + assertTrue(loader.privileged); + daemon.start(); + assertTrue(loader.hasState(true, true, false, false)); + daemon.stop(); + assertTrue(loader.hasState(true, true, true, false)); + daemon.destroy(); + assertTrue(loader.hasState(true, true, true, true)); + } + + @Test + public void requireThatNonPrivilegedLifecycleWorks() throws Exception { + MyLoader loader = new MyLoader(); + BootstrapDaemon daemon = new BootstrapDaemon(loader, false); + daemon.init(new MyContext("foo")); + assertTrue(loader.hasState(false, false, false, false)); + daemon.start(); + assertTrue(loader.hasState(true, true, false, false)); + assertFalse(loader.privileged); + daemon.stop(); + assertTrue(loader.hasState(true, true, true, false)); + daemon.destroy(); + assertTrue(loader.hasState(true, true, true, true)); + } + + @Test + public void requireThatBundleLocationIsRequired() throws Exception { + MyLoader loader = new MyLoader(); + BootstrapDaemon daemon = new BootstrapDaemon(loader, true); + try { + daemon.init(new MyContext((String[])null)); + fail(); + } catch (IllegalArgumentException e) { + assertNull(loader.bundleLocation); + } + try { + daemon.init(new MyContext()); + fail(); + } catch (IllegalArgumentException e) { + assertNull(loader.bundleLocation); + } + try { + daemon.init(new MyContext((String)null)); + fail(); + } catch (IllegalArgumentException e) { + assertNull(loader.bundleLocation); + } + try { + daemon.init(new MyContext("foo", "bar")); + fail(); + } catch (IllegalArgumentException e) { + assertNull(loader.bundleLocation); + } + + daemon.init(new MyContext("foo")); + daemon.start(); + + assertNotNull(loader.bundleLocation); + assertEquals("foo", loader.bundleLocation); + + daemon.stop(); + daemon.destroy(); + } + + @Test + public void requireThatEnvironmentIsRequired() { + try { + new BootstrapDaemon(); + fail(); + } catch (IllegalStateException e) { + + } + } + + private static class MyLoader implements BootstrapLoader { + + String bundleLocation = null; + boolean privileged = false; + boolean initCalled = false; + boolean startCalled = false; + boolean stopCalled = false; + boolean destroyCalled = false; + + boolean hasState(boolean initCalled, boolean startCalled, boolean stopCalled, boolean destroyCalled) { + return this.initCalled == initCalled && this.startCalled == startCalled && + this.stopCalled == stopCalled && this.destroyCalled == destroyCalled; + } + + @Override + public void init(String bundleLocation, boolean privileged) throws Exception { + this.bundleLocation = bundleLocation; + this.privileged = privileged; + initCalled = true; + } + + @Override + public void start() throws Exception { + startCalled = true; + } + + @Override + public void stop() throws Exception { + stopCalled = true; + } + + @Override + public void destroy() { + destroyCalled = true; + } + } + + private static class MyContext implements DaemonContext { + + final String[] args; + + MyContext(String... args) { + this.args = args; + } + + @Override + public DaemonController getController() { + return null; + } + + @Override + public String[] getArguments() { + return args; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/BundleLocationResolverTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/BundleLocationResolverTestCase.java new file mode 100644 index 00000000000..843ca91db68 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/BundleLocationResolverTestCase.java @@ -0,0 +1,87 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class BundleLocationResolverTestCase { + + @Test + public void requireThatDollarsAreIncludedInLocation() { + assertLocation("scheme:$foo", "scheme:$foo"); + assertLocation("scheme:foo$bar", "scheme:foo$bar"); + } + + @Test + public void requireThatCurlyBracesAreIncludedInLocation() { + assertLocation("scheme:{foo", "scheme:{foo"); + assertLocation("scheme:foo{", "scheme:foo{"); + assertLocation("scheme:foo{bar", "scheme:foo{bar"); + assertLocation("scheme:}foo", "scheme:}foo"); + assertLocation("scheme:foo}", "scheme:foo}"); + assertLocation("scheme:foo}bar", "scheme:foo}bar"); + assertLocation("scheme:{foo}bar", "scheme:{foo}bar"); + assertLocation("scheme:foo{bar}", "scheme:foo{bar}"); + } + + @Test + public void requireThatUnterminatedPropertiesAreIncludedInLocation() { + assertLocation("scheme:${foo", "scheme:${foo"); + assertLocation("scheme:foo${", "scheme:foo${"); + assertLocation("scheme:foo${bar", "scheme:foo${bar"); + } + + @Test + public void requireThatAllSystemPropertiesAreExpanded() throws IOException { + assertCanonicalPath("", "${foo}"); + assertCanonicalPath("barcox", "${foo}bar${baz}cox"); + assertCanonicalPath("foobaz", "foo${bar}baz${cox}"); + + System.setProperty("requireThatAllSystemPropertiesAreExpanded.foo", "FOO"); + System.setProperty("requireThatAllSystemPropertiesAreExpanded.bar", "BAR"); + System.setProperty("requireThatAllSystemPropertiesAreExpanded.baz", "BAZ"); + System.setProperty("requireThatAllSystemPropertiesAreExpanded.cox", "COX"); + assertCanonicalPath("FOO", "${requireThatAllSystemPropertiesAreExpanded.foo}"); + assertCanonicalPath("FOObarBAZcox", "${requireThatAllSystemPropertiesAreExpanded.foo}bar" + + "${requireThatAllSystemPropertiesAreExpanded.baz}cox"); + assertCanonicalPath("fooBARbazCOX", "foo${requireThatAllSystemPropertiesAreExpanded.bar}" + + "baz${requireThatAllSystemPropertiesAreExpanded.cox}"); + } + + @Test + public void requireThatUnschemedLocationsAreExpandedToBundleLocationProperty() throws IOException { + assertCanonicalPath(BundleLocationResolver.BUNDLE_PATH + "foo", "foo"); + } + + @Test + public void requireThatFileSchemedLocationsAreCanonicalized() throws IOException { + assertCanonicalPath("", "file:"); + assertCanonicalPath("foo", "file:foo"); + assertCanonicalPath("foo", "file:./foo"); + assertCanonicalPath("foo/bar", "file:foo/bar"); + assertCanonicalPath("foo/bar", "file:./foo/../foo/./bar"); + assertCanonicalPath("foo", " \f\n\r\tfile:foo"); + } + + @Test + public void requireThatOtherSchemedLocationsAreUntouched() { + assertLocation("foo:", "foo:"); + assertLocation("foo:bar", "foo:bar"); + assertLocation("foo:bar/baz", "foo:bar/baz"); + } + + private static void assertCanonicalPath(String expected, String bundleLocation) throws IOException { + assertLocation("file:" + new File(expected).getCanonicalPath(), bundleLocation); + } + + private static void assertLocation(String expected, String bundleLocation) { + assertEquals(expected, BundleLocationResolver.resolve(bundleLocation)); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogFormatterTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogFormatterTestCase.java new file mode 100644 index 00000000000..901817dbd26 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogFormatterTestCase.java @@ -0,0 +1,270 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import org.junit.Test; +import org.mockito.Mockito; +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceReference; +import org.osgi.service.log.LogEntry; +import org.osgi.service.log.LogService; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; + +import static org.junit.Assert.assertEquals; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class ConsoleLogFormatterTestCase { + + private static final ConsoleLogFormatter SIMPLE_FORMATTER = new ConsoleLogFormatter(null, null, null); + private static final LogEntry SIMPLE_ENTRY = new MyEntry(0, 0, null); + + // TODO: Should (at least) use ConsoleLogFormatter.ABSENCE_REPLACEMENT instead of literal '-'. See ticket 7128315. + + @Test + public void requireThatMillisecondsArePadded() { + for (int i = 0; i < 10000; ++i) { + LogEntry entry = new MyEntry(i, 0, null); + assertEquals(String.format("%d.%03d\t-\t-\t-\t-\tunknown\t", i / 1000, i % 1000), + SIMPLE_FORMATTER.formatEntry(entry)); + } + } + + @Test + public void requireThatHostNameIsIncluded() { + assertEquals("0.000\thostName\t-\t-\t-\tunknown\t", + new ConsoleLogFormatter("hostName", null, null).formatEntry(SIMPLE_ENTRY)); + } + + @Test + public void requireThatHostNameIsOptional() { + assertEquals("0.000\t-\t-\t-\t-\tunknown\t", + new ConsoleLogFormatter(null, null, null).formatEntry(SIMPLE_ENTRY)); + assertEquals("0.000\t-\t-\t-\t-\tunknown\t", + new ConsoleLogFormatter("", null, null).formatEntry(SIMPLE_ENTRY)); + assertEquals("0.000\t-\t-\t-\t-\tunknown\t", + new ConsoleLogFormatter(" ", null, null).formatEntry(SIMPLE_ENTRY)); + } + + @Test + public void requireThatProcessIdIsIncluded() { + assertEquals("0.000\t-\tprocessId\t-\t-\tunknown\t", + new ConsoleLogFormatter(null, "processId", null).formatEntry(SIMPLE_ENTRY)); + } + + @Test + public void requireThatProcessIdIsOptional() { + assertEquals("0.000\t-\t-\t-\t-\tunknown\t", + new ConsoleLogFormatter(null, null, null).formatEntry(SIMPLE_ENTRY)); + assertEquals("0.000\t-\t-\t-\t-\tunknown\t", + new ConsoleLogFormatter(null, "", null).formatEntry(SIMPLE_ENTRY)); + assertEquals("0.000\t-\t-\t-\t-\tunknown\t", + new ConsoleLogFormatter(null, " ", null).formatEntry(SIMPLE_ENTRY)); + } + + @Test + public void requireThatProcessIdIncludesThreadIdWhenAvailable() { + LogEntry entry = new MyEntry(0, 0, null).putProperty("THREAD_ID", "threadId"); + assertEquals("0.000\t-\tprocessId/threadId\t-\t-\tunknown\t", + new ConsoleLogFormatter(null, "processId", null).formatEntry(entry)); + } + + @Test + public void requireThatServiceNameIsIncluded() { + assertEquals("0.000\t-\t-\tserviceName\t-\tunknown\t", + new ConsoleLogFormatter(null, null, "serviceName").formatEntry(SIMPLE_ENTRY)); + } + + @Test + public void requireThatServiceNameIsOptional() { + assertEquals("0.000\t-\t-\t-\t-\tunknown\t", + new ConsoleLogFormatter(null, null, null).formatEntry(SIMPLE_ENTRY)); + assertEquals("0.000\t-\t-\t-\t-\tunknown\t", + new ConsoleLogFormatter(null, null, "").formatEntry(SIMPLE_ENTRY)); + assertEquals("0.000\t-\t-\t-\t-\tunknown\t", + new ConsoleLogFormatter(null, null, " ").formatEntry(SIMPLE_ENTRY)); + } + + @Test + public void requireThatBundleNameIsIncluded() { + LogEntry entry = new MyEntry(0, 0, null).setBundleSymbolicName("bundleName"); + assertEquals("0.000\t-\t-\t-\tbundleName\tunknown\t", + SIMPLE_FORMATTER.formatEntry(entry)); + } + + @Test + public void requireThatBundleNameIsOptional() { + assertEquals("0.000\t-\t-\t-\t-\tunknown\t", + SIMPLE_FORMATTER.formatEntry(SIMPLE_ENTRY)); + } + + @Test + public void requireThatLoggerNameIsIncluded() { + LogEntry entry = new MyEntry(0, 0, null).putProperty("LOGGER_NAME", "loggerName"); + assertEquals("0.000\t-\t-\t-\t/loggerName\tunknown\t", + SIMPLE_FORMATTER.formatEntry(entry)); + } + + @Test + public void requireThatLoggerNameIsOptional() { + assertEquals("0.000\t-\t-\t-\t-\tunknown\t", + SIMPLE_FORMATTER.formatEntry(SIMPLE_ENTRY)); + } + + @Test + public void requireThatBundleAndLoggerNameIsCombined() { + LogEntry entry = new MyEntry(0, 0, null).setBundleSymbolicName("bundleName") + .putProperty("LOGGER_NAME", "loggerName"); + assertEquals("0.000\t-\t-\t-\tbundleName/loggerName\tunknown\t", + SIMPLE_FORMATTER.formatEntry(entry)); + } + + @Test + public void requireThatLevelNameIsIncluded() { + ConsoleLogFormatter formatter = SIMPLE_FORMATTER; + assertEquals("0.000\t-\t-\t-\t-\terror\t", + formatter.formatEntry(new MyEntry(0, LogService.LOG_ERROR, null))); + assertEquals("0.000\t-\t-\t-\t-\twarning\t", + formatter.formatEntry(new MyEntry(0, LogService.LOG_WARNING, null))); + assertEquals("0.000\t-\t-\t-\t-\tinfo\t", + formatter.formatEntry(new MyEntry(0, LogService.LOG_INFO, null))); + assertEquals("0.000\t-\t-\t-\t-\tdebug\t", + formatter.formatEntry(new MyEntry(0, LogService.LOG_DEBUG, null))); + assertEquals("0.000\t-\t-\t-\t-\tunknown\t", + formatter.formatEntry(new MyEntry(0, 69, null))); + } + + @Test + public void requireThatMessageIsIncluded() { + LogEntry entry = new MyEntry(0, 0, "message"); + assertEquals("0.000\t-\t-\t-\t-\tunknown\tmessage", + SIMPLE_FORMATTER.formatEntry(entry)); + } + + @Test + public void requireThatMessageIsOptional() { + LogEntry entry = new MyEntry(0, 0, null); + assertEquals("0.000\t-\t-\t-\t-\tunknown\t", + SIMPLE_FORMATTER.formatEntry(entry)); + } + + @Test + public void requireThatMessageIsEscaped() { + LogEntry entry = new MyEntry(0, 0, "\\\n\r\t"); + assertEquals("0.000\t-\t-\t-\t-\tunknown\t\\\\\\n\\r\\t", + SIMPLE_FORMATTER.formatEntry(entry)); + } + + @Test + public void requireThatExceptionIsIncluded() { + Throwable t = new Throwable(); + LogEntry entry = new MyEntry(0, 0, null).setException(t); + assertEquals("0.000\t-\t-\t-\t-\tunknown\t\\n" + formatThrowable(t), + SIMPLE_FORMATTER.formatEntry(entry)); + } + + @Test + public void requireThatExceptionIsEscaped() { + Throwable t = new Throwable("\\\n\r\t"); + LogEntry entry = new MyEntry(0, 0, null).setException(t); + assertEquals("0.000\t-\t-\t-\t-\tunknown\t\\n" + formatThrowable(t), + SIMPLE_FORMATTER.formatEntry(entry)); + } + + @Test + public void requireThatExceptionIsSimplifiedForInfoEntries() { + Throwable t = new Throwable("exception"); + LogEntry entry = new MyEntry(0, LogService.LOG_INFO, "entry").setException(t); + assertEquals("0.000\t-\t-\t-\t-\tinfo\tentry: exception", + SIMPLE_FORMATTER.formatEntry(entry)); + } + + @Test + public void requireThatSimplifiedExceptionIsEscaped() { + Throwable t = new Throwable("\\\n\r\t"); + LogEntry entry = new MyEntry(0, LogService.LOG_INFO, "entry").setException(t); + assertEquals("0.000\t-\t-\t-\t-\tinfo\tentry: \\\\\\n\\r\\t", + SIMPLE_FORMATTER.formatEntry(entry)); + } + + @Test + public void requireThatSimplifiedExceptionMessageIsOptional() { + Throwable t = new Throwable(); + LogEntry entry = new MyEntry(0, LogService.LOG_INFO, "entry").setException(t); + assertEquals("0.000\t-\t-\t-\t-\tinfo\tentry: java.lang.Throwable", + SIMPLE_FORMATTER.formatEntry(entry)); + } + + private static String formatThrowable(Throwable t) { + Writer out = new StringWriter(); + t.printStackTrace(new PrintWriter(out)); + return out.toString().replace("\\", "\\\\").replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t"); + } + + private static class MyEntry implements LogEntry { + + final String message; + final int level; + final long time; + Bundle bundle = null; + ServiceReference serviceReference = null; + Throwable exception; + + MyEntry(long time, int level, String message) { + this.message = message; + this.level = level; + this.time = time; + } + + MyEntry setBundleSymbolicName(String symbolicName) { + this.bundle = Mockito.mock(Bundle.class); + Mockito.doReturn(symbolicName).when(this.bundle).getSymbolicName(); + return this; + } + + MyEntry setException(Throwable exception) { + this.exception = exception; + return this; + } + + MyEntry putProperty(String key, String val) { + this.serviceReference = Mockito.mock(ServiceReference.class); + Mockito.doReturn(val).when(this.serviceReference).getProperty(key); + return this; + } + + @Override + public long getTime() { + return time; + } + + @Override + public int getLevel() { + return level; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public Throwable getException() { + return exception; + } + + @Override + public Bundle getBundle() { + return bundle; + } + + @Override + public ServiceReference getServiceReference() { + return serviceReference; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogListenerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogListenerTestCase.java new file mode 100644 index 00000000000..3ac30f0456e --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogListenerTestCase.java @@ -0,0 +1,115 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import org.junit.Test; +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceReference; +import org.osgi.service.log.LogEntry; +import org.osgi.service.log.LogListener; +import org.osgi.service.log.LogService; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:vikasp@yahoo-inc.com">Vikas Panwar</a> + */ +public class ConsoleLogListenerTestCase { + + private static final String HOSTNAME = ConsoleLogFormatter.formatOptional(ConsoleLogListener.getHostname()); + private static final String PROCESS_ID = ConsoleLogListener.getProcessId(); + + @Test + public void requireThatLogLevelParserKnowsOsgiLogLevels() { + assertEquals(LogService.LOG_ERROR, ConsoleLogListener.parseLogLevel("ERROR")); + assertEquals(LogService.LOG_WARNING, ConsoleLogListener.parseLogLevel("WARNING")); + assertEquals(LogService.LOG_INFO, ConsoleLogListener.parseLogLevel("INFO")); + assertEquals(LogService.LOG_DEBUG, ConsoleLogListener.parseLogLevel("DEBUG")); + } + + @Test + public void requireThatLogLevelParserKnowsOff() { + assertEquals(Integer.MIN_VALUE, ConsoleLogListener.parseLogLevel("OFF")); + } + + @Test + public void requireThatLogLevelParserKnowsAll() { + assertEquals(Integer.MAX_VALUE, ConsoleLogListener.parseLogLevel("ALL")); + } + + @Test + public void requireThatLogLevelParserKnowsIntegers() { + for (int i = -69; i < 69; ++i) { + assertEquals(i, ConsoleLogListener.parseLogLevel(String.valueOf(i))); + } + } + + @Test + public void requireThatLogLevelParserErrorsReturnDefault() { + assertEquals(ConsoleLogListener.DEFAULT_LOG_LEVEL, ConsoleLogListener.parseLogLevel(null)); + assertEquals(ConsoleLogListener.DEFAULT_LOG_LEVEL, ConsoleLogListener.parseLogLevel("")); + assertEquals(ConsoleLogListener.DEFAULT_LOG_LEVEL, ConsoleLogListener.parseLogLevel("foo")); + } + + @Test + public void requireThatLogEntryWithLevelAboveThresholdIsNotOutput() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + LogListener listener = new ConsoleLogListener(new PrintStream(out), null, "5"); + for (int i = 0; i < 10; ++i) { + listener.logged(new MyEntry(0, i, "message")); + } + // TODO: Should use ConsoleLogFormatter.ABSENCE_REPLACEMENT instead of literal '-'. See ticket 7128315. + assertEquals("0.000\t" + HOSTNAME + "\t" + PROCESS_ID + "\t-\t-\tunknown\tmessage\n" + + "0.000\t" + HOSTNAME + "\t" + PROCESS_ID + "\t-\t-\terror\tmessage\n" + + "0.000\t" + HOSTNAME + "\t" + PROCESS_ID + "\t-\t-\twarning\tmessage\n" + + "0.000\t" + HOSTNAME + "\t" + PROCESS_ID + "\t-\t-\tinfo\tmessage\n" + + "0.000\t" + HOSTNAME + "\t" + PROCESS_ID + "\t-\t-\tdebug\tmessage\n" + + "0.000\t" + HOSTNAME + "\t" + PROCESS_ID + "\t-\t-\tunknown\tmessage\n", + out.toString()); + } + + private static class MyEntry implements LogEntry { + + final String message; + final int level; + final long time; + + MyEntry(long time, int level, String message) { + this.message = message; + this.level = level; + this.time = time; + } + + @Override + public long getTime() { + return time; + } + + @Override + public int getLevel() { + return level; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public Throwable getException() { + return null; + } + + @Override + public Bundle getBundle() { + return null; + } + + @Override + public ServiceReference getServiceReference() { + return null; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogManagerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogManagerTestCase.java new file mode 100644 index 00000000000..5bc1a29e2f7 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ConsoleLogManagerTestCase.java @@ -0,0 +1,90 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; +import org.mockito.Mockito; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.service.log.LogListener; +import org.osgi.service.log.LogReaderService; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class ConsoleLogManagerTestCase { + + @Test + public void requireThatManagerCanNotBeInstalledTwice() throws BundleException { + FelixFramework felix = TestDriver.newOsgiFramework(); + felix.start(); + + ConsoleLogManager manager = new ConsoleLogManager(); + manager.install(felix.bundleContext()); + try { + manager.install(felix.bundleContext()); + fail(); + } catch (IllegalStateException e) { + assertEquals("ConsoleLogManager already installed.", e.getMessage()); + } + + felix.stop(); + } + + @Test + public void requireThatManagerCanBeUninstalledTwice() throws BundleException { + FelixFramework felix = TestDriver.newOsgiFramework(); + felix.start(); + + ConsoleLogManager manager = new ConsoleLogManager(); + assertFalse(manager.uninstall()); + manager.install(felix.bundleContext()); + assertTrue(manager.uninstall()); + assertFalse(manager.uninstall()); + + felix.stop(); + } + + @Test + public void requireThatLogReaderServicesAreTracked() throws BundleException { + FelixFramework felix = TestDriver.newOsgiFramework(); + felix.start(); + BundleContext ctx = felix.bundleContext(); + + LogReaderService foo = Mockito.mock(LogReaderService.class); + ctx.registerService(LogReaderService.class.getName(), foo, null); + Mockito.verify(foo).addLogListener(Mockito.any(LogListener.class)); + + LogReaderService bar = Mockito.mock(LogReaderService.class); + ctx.registerService(LogReaderService.class.getName(), bar, null); + Mockito.verify(bar).addLogListener(Mockito.any(LogListener.class)); + + ConsoleLogManager manager = new ConsoleLogManager(); + manager.install(felix.bundleContext()); + + Mockito.verify(foo, Mockito.times(2)).addLogListener(Mockito.any(LogListener.class)); + Mockito.verify(bar, Mockito.times(2)).addLogListener(Mockito.any(LogListener.class)); + + LogReaderService baz = Mockito.mock(LogReaderService.class); + ctx.registerService(LogReaderService.class.getName(), baz, null); + Mockito.verify(baz, Mockito.times(2)).addLogListener(Mockito.any(LogListener.class)); + + assertTrue(manager.uninstall()); + + Mockito.verify(foo).removeLogListener(Mockito.any(LogListener.class)); + Mockito.verify(bar).removeLogListener(Mockito.any(LogListener.class)); + Mockito.verify(baz).removeLogListener(Mockito.any(LogListener.class)); + + felix.stop(); + + Mockito.verify(foo, Mockito.times(2)).removeLogListener(Mockito.any(LogListener.class)); + Mockito.verify(bar, Mockito.times(2)).removeLogListener(Mockito.any(LogListener.class)); + Mockito.verify(baz, Mockito.times(2)).removeLogListener(Mockito.any(LogListener.class)); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerResourceTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerResourceTestCase.java new file mode 100644 index 00000000000..11b6f27296d --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerResourceTestCase.java @@ -0,0 +1,162 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.yahoo.jdisc.Container; +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.ResourceReference; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.service.ServerProvider; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ContainerResourceTestCase { + + @Test + public void requireThatBoundRequestHandlersAreRetainedOnActivate() { + MyRequestHandler foo = new MyRequestHandler(); + MyRequestHandler bar = new MyRequestHandler(); + + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings("foo").bind("http://foo/", foo); + builder.serverBindings("bar").bind("http://bar/", bar); + assertEquals(0, foo.retainCnt.get()); + assertEquals(0, bar.retainCnt.get()); + + driver.activateContainer(builder); + assertEquals(1, foo.retainCnt.get()); + assertEquals(1, bar.retainCnt.get()); + assertTrue(driver.close()); + } + + @Test + public void requireThatBoundRequestHandlersAreReleasedOnTermination() { + MyRequestHandler handler = new MyRequestHandler(); + + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind("http://localhost/", handler); + driver.activateContainer(builder); + + Container container = driver.newReference(URI.create("http://localhost/")); + assertEquals(1, handler.retainCnt.get()); + driver.activateContainer(null); + assertEquals(1, handler.retainCnt.get()); + container.release(); + assertEquals(0, handler.retainCnt.get()); + + assertTrue(driver.close()); + } + + @Test + public void requireThatServerProvidersAreRetainedOnActivate() { + MyServerProvider foo = new MyServerProvider(); + MyServerProvider bar = new MyServerProvider(); + + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverProviders().install(foo); + builder.serverProviders().install(bar); + assertEquals(0, foo.retainCnt.get()); + assertEquals(0, bar.retainCnt.get()); + + driver.activateContainer(builder); + assertEquals(1, foo.retainCnt.get()); + assertEquals(1, bar.retainCnt.get()); + assertTrue(driver.close()); + } + + @Test + public void requireThatServerProvidersAreReleasedOnTermination() { + MyServerProvider server = new MyServerProvider(); + + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverProviders().install(server); + driver.activateContainer(builder); + + Container container = driver.newReference(URI.create("http://localhost/")); + assertEquals(1, server.retainCnt.get()); + driver.activateContainer(null); + assertEquals(1, server.retainCnt.get()); + container.release(); + assertEquals(0, server.retainCnt.get()); + + assertTrue(driver.close()); + } + + private static class MyRequestHandler implements RequestHandler { + + final AtomicInteger retainCnt = new AtomicInteger(0); + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + throw new UnsupportedOperationException(); + } + + @Override + public void handleTimeout(Request request, ResponseHandler handler) { + throw new UnsupportedOperationException(); + } + + @Override + public ResourceReference refer() { + retainCnt.incrementAndGet(); + return new ResourceReference() { + @Override + public void close() { + retainCnt.decrementAndGet(); + } + }; + } + + @Override + public void release() { + retainCnt.decrementAndGet(); + } + } + + private static class MyServerProvider implements ServerProvider { + + final AtomicInteger retainCnt = new AtomicInteger(0); + + @Override + public void start() { + + } + + @Override + public void close() { + + } + + @Override + public ResourceReference refer() { + retainCnt.incrementAndGet(); + return new ResourceReference() { + @Override + public void close() { + retainCnt.decrementAndGet(); + } + }; + } + + @Override + public void release() { + retainCnt.decrementAndGet(); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerShutdownTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerShutdownTestCase.java new file mode 100644 index 00000000000..488628867b4 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerShutdownTestCase.java @@ -0,0 +1,848 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.yahoo.jdisc.Container; +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.handler.AbstractRequestHandler; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; +import java.nio.ByteBuffer; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ContainerShutdownTestCase { + + @Test + public void requireThatContainerBlocksTermination() { + Context ctx = Context.newInstance(); + Container container = ctx.driver.newReference(URI.create("http://host/path")); + assertFalse(ctx.shutdown()); + container.release(); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatNewRequestBlocksTermination() { + Context ctx = Context.newPendingRequest(MyRequestHandler.newInstance()); + assertFalse(ctx.shutdown()); + ctx.request.release(); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatOpenRequestBlocksTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + ContentChannel requestContent = ctx.request.connect(MyResponseHandler.newEagerCompletion()); + ctx.request.release(); + requestHandler.respond().close(null); + assertFalse(ctx.shutdown()); + requestContent.close(null); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatResponsePendingBlocksTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + ctx.request.connect(MyResponseHandler.newEagerCompletion()).close(null); + ctx.request.release(); + assertFalse(ctx.shutdown()); + requestHandler.respond().close(null); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatOpenResponseBlocksTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + ctx.request.connect(MyResponseHandler.newEagerCompletion()).close(null); + ctx.request.release(); + ContentChannel responseContent = requestHandler.respond(); + assertFalse(ctx.shutdown()); + responseContent.close(null); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestExceptionDoesNotBlockTermination() { + Context ctx = Context.newPendingRequest(MyRequestHandler.newRequestException()); + try { + ctx.request.connect(MyResponseHandler.newEagerCompletion()); + fail(); + } catch (MyException e) { + // ignore + } + ctx.request.release(); + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestExceptionWithEagerHandleResponseBlocksTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newRequestExceptionWithEagerHandleResponse(); + Context ctx = Context.newPendingRequest(requestHandler); + try { + ctx.request.connect(MyResponseHandler.newEagerCompletion()); + fail(); + } catch (MyException e) { + // ignore + } + ctx.request.release(); + assertFalse(ctx.shutdown()); + requestHandler.responseContent.close(null); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestExceptionWithEagerCloseResponseDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newRequestExceptionWithEagerCloseResponse(); + Context ctx = Context.newPendingRequest(requestHandler); + try { + ctx.request.connect(MyResponseHandler.newEagerCompletion()); + fail(); + } catch (MyException e) { + // ignore + } + ctx.request.release(); + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatNullRequestContentBlocksTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newNullContent(); + Context ctx = Context.newPendingRequest(requestHandler); + ctx.request.connect(MyResponseHandler.newEagerCompletion()).close(null); + ctx.request.release(); + assertFalse(ctx.shutdown()); + requestHandler.respond(); + requestHandler.responseContent.close(null); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatNullRequestContentWithEagerHandleResponseBlocksTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newNullContentWithEagerHandleResponse(); + Context ctx = Context.newPendingRequest(requestHandler); + ctx.request.connect(MyResponseHandler.newEagerCompletion()).close(null); + ctx.request.release(); + assertFalse(ctx.shutdown()); + requestHandler.responseContent.close(null); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatNullRequestContentWithEagerCloseResponseBlocksTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newNulContentWithEagerCloseResponse(); + Context ctx = Context.newPendingRequest(requestHandler); + ContentChannel requestContent = ctx.request.connect(MyResponseHandler.newEagerCompletion()); + ctx.request.release(); + assertFalse(ctx.shutdown()); + requestContent.close(null); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestContentWriteFailedDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerFail(); + Context ctx = Context.newPendingRequest(requestHandler); + ContentChannel requestContent = ctx.request.connect(MyResponseHandler.newEagerCompletion()); + requestContent.write(ByteBuffer.allocate(69), null); + requestContent.close(null); + ctx.request.release(); + requestHandler.respond().close(null); + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestContentWriteExceptionDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newContentWriteExceptionWithEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + ContentChannel requestContent = ctx.request.connect(MyResponseHandler.newEagerCompletion()); + try { + requestContent.write(ByteBuffer.allocate(69), null); + fail(); + } catch (MyException e) { + // ignore + } + requestContent.close(null); + ctx.request.release(); + requestHandler.respond().close(null); + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestContentWriteExceptionDoesNotForceTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newContentWriteExceptionWithEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + ContentChannel requestContent = ctx.request.connect(MyResponseHandler.newEagerCompletion()); + try { + requestContent.write(ByteBuffer.allocate(69), null); + fail(); + } catch (MyException e) { + // ignore + } + ctx.request.release(); + requestHandler.respond().close(null); + assertFalse(ctx.shutdown()); + requestContent.close(null); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestContentWriteExceptionWithCompletionDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newContentWriteExceptionWithEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + ContentChannel requestContent = ctx.request.connect(MyResponseHandler.newEagerCompletion()); + try { + requestContent.write(ByteBuffer.allocate(69), MyCompletion.newInstance()); + fail(); + } catch (MyException e) { + // ignore + } + requestContent.close(null); + ctx.request.release(); + requestHandler.respond().close(null); + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestContentCloseFailedDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerFail(); + Context ctx = Context.newPendingRequest(requestHandler); + ContentChannel requestContent = ctx.request.connect(MyResponseHandler.newEagerCompletion()); + requestContent.close(null); + ctx.request.release(); + requestHandler.respond().close(null); + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestContentCloseExceptionDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newContentCloseException(); + Context ctx = Context.newPendingRequest(requestHandler); + ContentChannel requestContent = ctx.request.connect(MyResponseHandler.newEagerCompletion()); + try { + requestContent.close(null); + fail(); + } catch (MyException e) { + // ignore + } + ctx.request.release(); + requestHandler.respond().close(null); + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestContentCloseExceptionWithCompletionDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newContentCloseException(); + Context ctx = Context.newPendingRequest(requestHandler); + ContentChannel requestContent = ctx.request.connect(MyResponseHandler.newEagerCompletion()); + try { + requestContent.close(MyCompletion.newInstance()); + fail(); + } catch (MyException e) { + // ignore + } + ctx.request.release(); + requestHandler.respond().close(null); + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestWriteCompletionBlocksTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCloseResponse(); + Context ctx = Context.newPendingRequest(requestHandler); + ContentChannel requestContent = ctx.request.connect(MyResponseHandler.newEagerCompletion()); + ctx.request.release(); + requestContent.write(null, MyCompletion.newInstance()); + requestContent.close(null); + requestHandler.requestContent.closeCompletion.completed(); + assertFalse(ctx.shutdown()); + requestHandler.requestContent.writeCompletion.completed(); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestWriteCompletionExceptionDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCloseResponse(); + Context ctx = Context.newPendingRequest(requestHandler); + ContentChannel requestContent = ctx.request.connect(MyResponseHandler.newEagerCompletion()); + ctx.request.release(); + requestContent.write(null, MyCompletion.newException()); + requestContent.close(null); + requestHandler.requestContent.closeCompletion.completed(); + assertFalse(ctx.shutdown()); + try { + requestHandler.requestContent.writeCompletion.completed(); + fail(); + } catch (MyException e) { + // ignore + } + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestCloseCompletionBlocksTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCloseResponse(); + Context ctx = Context.newPendingRequest(requestHandler); + ContentChannel requestContent = ctx.request.connect(MyResponseHandler.newEagerCompletion()); + ctx.request.release(); + requestContent.close(MyCompletion.newInstance()); + assertFalse(ctx.shutdown()); + requestHandler.requestContent.closeCompletion.completed(); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatRequestCloseCompletionExceptionDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCloseResponse(); + Context ctx = Context.newPendingRequest(requestHandler); + ContentChannel requestContent = ctx.request.connect(MyResponseHandler.newEagerCompletion()); + ctx.request.release(); + requestContent.close(MyCompletion.newException()); + assertFalse(ctx.shutdown()); + try { + requestHandler.requestContent.closeCompletion.completed(); + fail(); + } catch (MyException e) { + // ignore + } + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatNullResponseContentBlocksTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerRespondWithEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + MyResponseHandler responseHandler = MyResponseHandler.newNullContent(); + ctx.request.connect(responseHandler).close(null); + ctx.request.release(); + + assertFalse(ctx.shutdown()); + requestHandler.responseContent.close(MyCompletion.newInstance()); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatResponseExceptionDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + ctx.request.connect(MyResponseHandler.newResponseException()).close(null); + ctx.request.release(); + try { + requestHandler.respond(); + fail(); + } catch (MyException e) { + // ignore + } + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatResponseContentWriteFailedDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + MyResponseHandler responseHandler = MyResponseHandler.newEagerFail(); + ctx.request.connect(responseHandler).close(null); + ctx.request.release(); + requestHandler.respond(); + requestHandler.responseContent.write(ByteBuffer.allocate(69), null); + requestHandler.responseContent.close(null); + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatResponseContentCloseFailedDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + MyResponseHandler responseHandler = MyResponseHandler.newEagerFail(); + ctx.request.connect(responseHandler).close(null); + ctx.request.release(); + requestHandler.respond(); + requestHandler.responseContent.close(null); + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatResponseContentWriteExceptionDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + MyResponseHandler responseHandler = MyResponseHandler.newContentWriteException(); + ctx.request.connect(responseHandler).close(null); + ctx.request.release(); + requestHandler.respond(); + try { + requestHandler.responseContent.write(ByteBuffer.allocate(69), null); + fail(); + } catch (MyException e) { + // ignore + } + requestHandler.responseContent.close(null); + responseHandler.content.closeCompletion.completed(); + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatResponseContentWriteExceptionDoesNotForceTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + MyResponseHandler responseHandler = MyResponseHandler.newContentWriteException(); + ctx.request.connect(responseHandler).close(null); + ctx.request.release(); + requestHandler.respond(); + try { + requestHandler.responseContent.write(ByteBuffer.allocate(69), null); + fail(); + } catch (MyException e) { + // ignore + } + assertFalse(ctx.shutdown()); + requestHandler.responseContent.close(null); + responseHandler.content.closeCompletion.completed(); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatResponseContentWriteExceptionWithCompletionDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + MyResponseHandler responseHandler = MyResponseHandler.newContentWriteException(); + ctx.request.connect(responseHandler).close(null); + ctx.request.release(); + requestHandler.respond(); + try { + requestHandler.responseContent.write(ByteBuffer.allocate(69), MyCompletion.newInstance()); + fail(); + } catch (MyException e) { + // ignore + } + requestHandler.responseContent.close(null); + responseHandler.content.closeCompletion.completed(); + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatResponseContentCloseExceptionDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + ctx.request.connect(MyResponseHandler.newContentCloseException()).close(null); + ctx.request.release(); + try { + requestHandler.respond().close(null); + fail(); + } catch (MyException e) { + // ignore + } + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatResponseContentCloseExceptionWithCompletionDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + ctx.request.connect(MyResponseHandler.newContentCloseException()).close(null); + ctx.request.release(); + try { + requestHandler.respond().close(MyCompletion.newInstance()); + fail(); + } catch (MyException e) { + // ignore + } + assertTrue(ctx.shutdown()); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatResponseWriteCompletionBlocksTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerRespondWithEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + MyResponseHandler responseHandler = MyResponseHandler.newInstance(); + ctx.request.connect(responseHandler).close(null); + ctx.request.release(); + + requestHandler.responseContent.write(null, MyCompletion.newInstance()); + requestHandler.responseContent.close(null); + responseHandler.content.closeCompletion.completed(); + assertFalse(ctx.shutdown()); + responseHandler.content.writeCompletion.completed(); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatResponseWriteCompletionExceptionDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerRespondWithEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + MyResponseHandler responseHandler = MyResponseHandler.newInstance(); + ctx.request.connect(responseHandler).close(null); + ctx.request.release(); + + requestHandler.responseContent.write(null, MyCompletion.newException()); + requestHandler.responseContent.close(null); + responseHandler.content.closeCompletion.completed(); + assertFalse(ctx.shutdown()); + try { + responseHandler.content.writeCompletion.completed(); + fail(); + } catch (MyException e) { + // ignore + } + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatResponseCloseCompletionBlocksTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerRespondWithEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + MyResponseHandler responseHandler = MyResponseHandler.newInstance(); + ctx.request.connect(responseHandler).close(null); + ctx.request.release(); + + requestHandler.responseContent.close(MyCompletion.newInstance()); + assertFalse(ctx.shutdown()); + responseHandler.content.closeCompletion.completed(); + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + @Test + public void requireThatResponseCloseCompletionExceptionDoesNotBlockTermination() { + MyRequestHandler requestHandler = MyRequestHandler.newEagerRespondWithEagerCompletion(); + Context ctx = Context.newPendingRequest(requestHandler); + MyResponseHandler responseHandler = MyResponseHandler.newInstance(); + ctx.request.connect(responseHandler).close(null); + ctx.request.release(); + + requestHandler.responseContent.close(MyCompletion.newException()); + assertFalse(ctx.shutdown()); + try { + responseHandler.content.closeCompletion.completed(); + fail(); + } catch (MyException e) { + // ignore + } + assertTrue(ctx.terminated); + assertTrue(ctx.driver.close()); + } + + private static class Context { + + final TestDriver driver; + final Request request; + boolean terminated = false; + + Context(TestDriver driver, Request request) { + this.driver = driver; + this.request = request; + } + + boolean shutdown() { + driver.activateContainer(null).notifyTermination(new Runnable() { + + @Override + public void run() { + terminated = true; + } + }); + return terminated; + } + + static Context newInstance() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + driver.activateContainer(driver.newContainerBuilder()); + return new Context(driver, null); + } + + static Context newPendingRequest(RequestHandler requestHandler) { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind("http://host/path", requestHandler); + driver.activateContainer(builder); + return new Context(driver, new Request(driver, URI.create("http://host/path"))); + } + } + + private static class MyCompletion implements CompletionHandler { + + final boolean throwException; + + MyCompletion(boolean throwException) { + this.throwException = throwException; + } + + @Override + public void completed() { + if (throwException) { + throw new MyException(); + } + } + + @Override + public void failed(Throwable t) { + if (throwException) { + throw new MyException(); + } + } + + static MyCompletion newInstance() { + return new MyCompletion(false); + } + + static MyCompletion newException() { + return new MyCompletion(true); + } + } + + private static class MyContent implements ContentChannel { + + final boolean eagerCompletion; + final boolean eagerFail; + final boolean writeException; + final boolean closeException; + CompletionHandler writeCompletion = null; + CompletionHandler closeCompletion = null; + + MyContent(boolean eagerCompletion, boolean eagerFail, boolean writeException, boolean closeException) { + this.eagerCompletion = eagerCompletion; + this.eagerFail = eagerFail; + this.writeException = writeException; + this.closeException = closeException; + } + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + writeCompletion = handler; + if (eagerCompletion) { + writeCompletion.completed(); + } else if (eagerFail) { + writeCompletion.failed(new MyException()); + } + if (writeException) { + throw new MyException(); + } + } + + @Override + public void close(CompletionHandler handler) { + closeCompletion = handler; + if (eagerCompletion) { + closeCompletion.completed(); + } else if (eagerFail) { + closeCompletion.failed(new MyException()); + } + if (closeException) { + throw new MyException(); + } + } + + static MyContent newInstance() { + return new MyContent(false, false, false, false); + } + + static MyContent newEagerCompletion() { + return new MyContent(true, false, false, false); + } + + static MyContent newEagerFail() { + return new MyContent(false, true, false, false); + } + + static MyContent newWriteException() { + return new MyContent(false, false, true, false); + } + + static MyContent newWriteExceptionWithEagerCompletion() { + return new MyContent(true, false, true, false); + } + + static MyContent newCloseException() { + return new MyContent(false, false, false, true); + } + } + + private static class MyRequestHandler extends AbstractRequestHandler { + + final MyContent requestContent; + final boolean eagerRespond; + final boolean closeResponse; + final boolean throwException; + ContentChannel responseContent = null; + ResponseHandler handler = null; + + MyRequestHandler(MyContent requestContent, boolean eagerRespond, boolean closeResponse, + boolean throwException) + { + this.requestContent = requestContent; + this.eagerRespond = eagerRespond; + this.closeResponse = closeResponse; + this.throwException = throwException; + } + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + this.handler = handler; + if (eagerRespond) { + respond(); + } + if (throwException) { + throw new MyException(); + } + return requestContent; + } + + ContentChannel respond() { + responseContent = handler.handleResponse(new Response(Response.Status.OK)); + if (responseContent != null && closeResponse) { + responseContent.close(null); + } + return responseContent; + } + + static MyRequestHandler newInstance() { + return new MyRequestHandler(MyContent.newInstance(), false, false, false); + } + + static MyRequestHandler newEagerCompletion() { + return new MyRequestHandler(MyContent.newEagerCompletion(), false, false, false); + } + + static MyRequestHandler newEagerFail() { + return new MyRequestHandler(MyContent.newEagerFail(), false, false, false); + } + + static RequestHandler newRequestException() { + return new MyRequestHandler(null, false, false, true); + } + + static MyRequestHandler newNullContent() { + return new MyRequestHandler(null, false, false, false); + } + + static MyRequestHandler newNullContentWithEagerHandleResponse() { + return new MyRequestHandler(null, true, false, false); + } + + static MyRequestHandler newNulContentWithEagerCloseResponse() { + return new MyRequestHandler(null, true, true, false); + } + + static MyRequestHandler newRequestExceptionWithEagerHandleResponse() { + return new MyRequestHandler(null, true, false, true); + } + + static MyRequestHandler newRequestExceptionWithEagerCloseResponse() { + return new MyRequestHandler(null, true, true, true); + } + + static MyRequestHandler newContentWriteExceptionWithEagerCompletion() { + return new MyRequestHandler(MyContent.newWriteExceptionWithEagerCompletion(), true, true, false); + } + + static MyRequestHandler newContentCloseException() { + return new MyRequestHandler(MyContent.newCloseException(), true, true, false); + } + + static MyRequestHandler newEagerRespondWithEagerCompletion() { + return new MyRequestHandler(MyContent.newEagerCompletion(), true, false, false); + } + + static MyRequestHandler newEagerCloseResponse() { + return new MyRequestHandler(MyContent.newInstance(), true, true, false); + } + } + + private static class MyResponseHandler implements ResponseHandler { + + final MyContent content; + final boolean throwException; + + MyResponseHandler(MyContent content, boolean throwException) { + this.content = content; + this.throwException = throwException; + } + + @Override + public ContentChannel handleResponse(Response response) { + if (throwException) { + throw new MyException(); + } + return content; + } + + static MyResponseHandler newInstance() { + return new MyResponseHandler(MyContent.newInstance(), false); + } + + static MyResponseHandler newEagerCompletion() { + return new MyResponseHandler(MyContent.newEagerCompletion(), false); + } + + static MyResponseHandler newEagerFail() { + return new MyResponseHandler(MyContent.newEagerFail(), false); + } + + static MyResponseHandler newNullContent() { + return new MyResponseHandler(null, false); + } + + static MyResponseHandler newResponseException() { + return new MyResponseHandler(null, true); + } + + static MyResponseHandler newContentWriteException() { + return new MyResponseHandler(MyContent.newWriteException(), false); + } + + static MyResponseHandler newContentCloseException() { + return new MyResponseHandler(MyContent.newCloseException(), false); + } + } + + private static final class MyException extends RuntimeException { + + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerSnapshotTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerSnapshotTestCase.java new file mode 100644 index 00000000000..15bd3b050d4 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerSnapshotTestCase.java @@ -0,0 +1,211 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.google.inject.AbstractModule; +import com.google.inject.Key; +import com.google.inject.name.Names; +import com.yahoo.jdisc.AbstractResource; +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.application.BindingMatch; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; +import java.nio.ByteBuffer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ContainerSnapshotTestCase { + + @Test + public void requireThatServerHandlerCanBeResolved() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind("http://foo/*", MyRequestHandler.newInstance()); + driver.activateContainer(builder); + + Request request = new Request(driver, URI.create("http://foo/")); + assertNotNull(request.container().resolveHandler(request)); + assertNotNull(request.getBindingMatch()); + request.release(); + + request = new Request(driver, URI.create("http://foo/")); + request.setServerRequest(false); + assertNull(request.container().resolveHandler(request)); + assertNull(request.getBindingMatch()); + request.release(); + + request = new Request(driver, URI.create("http://bar/")); + assertNull(request.container().resolveHandler(request)); + assertNull(request.getBindingMatch()); + request.release(); + + request = new Request(driver, URI.create("http://bar/")); + request.setServerRequest(false); + assertNull(request.container().resolveHandler(request)); + assertNull(request.getBindingMatch()); + request.release(); + + assertTrue(driver.close()); + } + + @Test + public void requireThatClientHandlerCanBeResolved() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.clientBindings().bind("http://foo/*", MyRequestHandler.newInstance()); + driver.activateContainer(builder); + + Request request = new Request(driver, URI.create("http://foo/")); + assertNull(request.container().resolveHandler(request)); + assertNull(request.getBindingMatch()); + request.release(); + + request = new Request(driver, URI.create("http://foo/")); + request.setServerRequest(false); + assertNotNull(request.container().resolveHandler(request)); + assertNotNull(request.getBindingMatch()); + request.release(); + + request = new Request(driver, URI.create("http://bar/")); + assertNull(request.container().resolveHandler(request)); + assertNull(request.getBindingMatch()); + request.release(); + + request = new Request(driver, URI.create("http://bar/")); + request.setServerRequest(false); + assertNull(request.container().resolveHandler(request)); + assertNull(request.getBindingMatch()); + request.release(); + + assertTrue(driver.close()); + } + + @Test + public void requireThatClientBindingsAreUsed() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.clientBindings().bind("http://host/path", MyRequestHandler.newInstance()); + driver.activateContainer(builder); + Request request = new Request(driver, URI.create("http://host/path")); + assertNull(request.container().resolveHandler(request)); + request.setServerRequest(false); + assertNotNull(request.container().resolveHandler(request)); + request.release(); + assertTrue(driver.close()); + } + + @Test + public void requireThatBindingMatchIsSetByResolveHandler() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind("http://*/*", MyRequestHandler.newInstance()); + driver.activateContainer(builder); + + Request request = new Request(driver, URI.create("http://localhost:69/status.html")); + assertNotNull(request.container().resolveHandler(request)); + BindingMatch<RequestHandler> match = request.getBindingMatch(); + assertNotNull(match); + assertEquals(3, match.groupCount()); + assertEquals("localhost", match.group(0)); + assertEquals("69", match.group(1)); + assertEquals("status.html", match.group(2)); + request.release(); + + assertTrue(driver.close()); + } + + @Test + public void requireThatNewRequestHasSameSnapshot() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + driver.activateContainer(driver.newContainerBuilder()); + Request foo = new Request(driver, URI.create("http://host/foo")); + Request bar = new Request(foo, URI.create("http://host/bar")); + assertSame(foo.container(), bar.container()); + foo.release(); + bar.release(); + assertTrue(driver.close()); + } + + @Test + public void requireThatActiveInjectorIsUsed() { + final Object obj = new Object(); + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(new AbstractModule() { + + @Override + protected void configure() { + bind(Object.class).toInstance(obj); + bind(String.class).annotatedWith(Names.named("foo")).toInstance("foo"); + } + }); + ActiveContainer active = new ActiveContainer(driver.newContainerBuilder()); + ContainerSnapshot snapshot = new ContainerSnapshot(active, null, null); + assertSame(obj, snapshot.getInstance(Object.class)); + assertEquals("foo", snapshot.getInstance(Key.get(String.class, Names.named("foo")))); + snapshot.release(); + assertTrue(driver.close()); + } + + private static class MyContent implements ContentChannel { + + CompletionHandler writeCompletion = null; + CompletionHandler closeCompletion = null; + ByteBuffer writeBuf = null; + boolean closed = false; + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + writeBuf = buf; + writeCompletion = handler; + } + + @Override + public void close(CompletionHandler handler) { + closed = true; + closeCompletion = handler; + } + } + + private static class MyRequestHandler extends AbstractResource implements RequestHandler { + + final MyContent content = new MyContent(); + Request request = null; + ResponseHandler handler = null; + boolean timeout = false; + boolean destroyed = false; + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + this.request = request; + this.handler = handler; + return content; + } + + @Override + public void handleTimeout(Request request, ResponseHandler handler) { + timeout = true; + } + + @Override + public void destroy() { + destroyed = true; + } + + static MyRequestHandler newInstance() { + return new MyRequestHandler(); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerTerminationTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerTerminationTestCase.java new file mode 100644 index 00000000000..bc2591d94b0 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ContainerTerminationTestCase.java @@ -0,0 +1,76 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.application.DeactivatedContainer; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ContainerTerminationTestCase { + + @Test + public void requireThatAccessorsWork() { + Object obj = new Object(); + ContainerTermination termination = new ContainerTermination(obj); + assertSame(obj, termination.appContext()); + } + + @Test + public void requireThatAppContextIsFromBuilder() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + Object obj = new Object(); + builder.setAppContext(obj); + driver.activateContainer(builder); + DeactivatedContainer container = driver.activateContainer(null); + assertSame(obj, container.appContext()); + assertTrue(driver.close()); + } + + @Test + public void requireThatEarlyTerminationIsNotified() { + ContainerTermination termination = new ContainerTermination(null); + termination.run(); + MyTask task = new MyTask(); + termination.notifyTermination(task); + assertTrue(task.done); + } + + @Test + public void requireThatLaterTerminationIsNotified() { + ContainerTermination termination = new ContainerTermination(null); + MyTask task = new MyTask(); + termination.notifyTermination(task); + assertFalse(task.done); + termination.run(); + assertTrue(task.done); + } + + @Test + public void requireThatNotifyCanOnlyBeCalledOnce() { + ContainerTermination termination = new ContainerTermination(null); + termination.notifyTermination(new MyTask()); + try { + termination.notifyTermination(new MyTask()); + } catch (IllegalStateException e) { + + } + } + + private static class MyTask implements Runnable { + + boolean done = false; + + @Override + public void run() { + done = true; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/DefaultBindingSelectorTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/DefaultBindingSelectorTestCase.java new file mode 100644 index 00000000000..1154d01dfe5 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/DefaultBindingSelectorTestCase.java @@ -0,0 +1,38 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.google.inject.Guice; +import com.yahoo.jdisc.application.BindingSet; +import com.yahoo.jdisc.application.BindingSetSelector; +import org.junit.Test; + +import java.net.URI; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class DefaultBindingSelectorTestCase { + + @Test + public void requireThatClassIsInjectedByDefault() { + BindingSetSelector selector = Guice.createInjector().getInstance(BindingSetSelector.class); + assertTrue(selector instanceof DefaultBindingSelector); + } + + @Test + public void requireThatDefaultSetIsAlwaysSelected() { + DefaultBindingSelector selector = new DefaultBindingSelector(); + assertEquals(BindingSet.DEFAULT, selector.select(null)); + for (int i = 0; i < 69; ++i) { + assertEquals(BindingSet.DEFAULT, selector.select(newUri())); + } + } + + private static URI newUri() { + return URI.create("foo" + System.nanoTime() + "://bar" + System.nanoTime() + "/baz" + System.nanoTime()); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ExportPackagesTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ExportPackagesTestCase.java new file mode 100644 index 00000000000..a61ee4efc2d --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ExportPackagesTestCase.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.jdisc.core; + +import org.junit.Test; + +import java.io.File; +import java.io.FileReader; +import java.util.Properties; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class ExportPackagesTestCase { + + @Test + public void requireThatPropertiesAreWritten() throws Exception { + File file = new File("target", ExportPackages.PROPERTIES_FILE); + file.deleteOnExit(); + ExportPackages.main(new String[] { file.getAbsolutePath() }); + assertTrue(file.exists()); + Properties props = new Properties(); + try (FileReader reader = new FileReader(file)) { + props.load(reader); + assertNotNull(props.getProperty(ExportPackages.EXPORT_PACKAGES)); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixFrameworkTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixFrameworkTestCase.java new file mode 100644 index 00000000000..69696ff62ec --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixFrameworkTestCase.java @@ -0,0 +1,41 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; +import org.osgi.framework.BundleException; + +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class FelixFrameworkTestCase { + + @Test + public void requireThatLifecycleWorks() throws BundleException { + FelixFramework felix = TestDriver.newOsgiFramework(); + felix.start(); + felix.stop(); + } + + @Test + public void requireThatStopWithoutStartDoesNotThrowException() throws BundleException { + FelixFramework felix = TestDriver.newOsgiFramework(); + felix.stop(); + } + + @Test + public void requireThatInstallCanThrowException() throws BundleException { + FelixFramework felix = TestDriver.newOsgiFramework(); + felix.start(); + try { + felix.installBundle("file:notfound.jar"); + fail(); + } catch (BundleException e) { + + } + felix.stop(); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixParamsTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixParamsTestCase.java new file mode 100644 index 00000000000..216b79c1d7f --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/FelixParamsTestCase.java @@ -0,0 +1,67 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import org.junit.Test; +import org.osgi.framework.Constants; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class FelixParamsTestCase { + + @Test + public void requireThatAccessorsWork() { + FelixParams params = new FelixParams(); + params.setCachePath("foo"); + assertEquals("foo", params.getCachePath()); + params.setLoggerEnabled(true); + assertTrue(params.isLoggerEnabled()); + } + + @Test + public void requireThatSystemPackagesAreNotReplaced() { + FelixParams params = new FelixParams(); + Map<String, String> config = params.toConfig(); + assertNotNull(config); + String str = config.get(Constants.FRAMEWORK_SYSTEMPACKAGES); + assertNotNull(str); + assertTrue(str.contains(ExportPackages.getSystemPackages())); + + params.exportPackage("foo"); + assertNotNull(config = params.toConfig()); + assertNotNull(str = config.get(Constants.FRAMEWORK_SYSTEMPACKAGES)); + assertTrue(str.contains(ExportPackages.getSystemPackages())); + assertTrue(str.contains("foo")); + } + + @Test + public void requireThatExportsAreIncludedInConfig() { + FelixParams params = new FelixParams(); + Map<String, String> config = params.toConfig(); + assertNotNull(config); + String[] prev = config.get(Constants.FRAMEWORK_SYSTEMPACKAGES).split(","); + + params.exportPackage("foo"); + params.exportPackage("bar"); + assertNotNull(config = params.toConfig()); + String[] next = config.get(Constants.FRAMEWORK_SYSTEMPACKAGES).split(","); + + assertEquals(prev.length + 2, next.length); + + List<String> diff = new LinkedList<>(); + diff.addAll(Arrays.asList(next)); + diff.removeAll(Arrays.asList(prev)); + assertEquals(2, diff.size()); + assertTrue(diff.contains("foo")); + assertTrue(diff.contains("bar")); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogHandlerTestCase.java new file mode 100644 index 00000000000..eb18e6f8e49 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogHandlerTestCase.java @@ -0,0 +1,192 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import org.junit.Test; +import org.osgi.framework.ServiceReference; +import org.osgi.service.log.LogService; + +import java.util.Arrays; +import java.util.Enumeration; +import java.util.ResourceBundle; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class OsgiLogHandlerTestCase { + + @Test + public void requireThatLogRecordsArePublishedToLogService() { + MyLogService logService = new MyLogService(); + Logger log = newLogger(logService); + + log.log(Level.INFO, "foo"); + assertEquals(OsgiLogHandler.toServiceLevel(Level.INFO), logService.lastLevel); + assertEquals("foo", logService.lastMessage); + assertNull(logService.lastThrowable); + + Throwable t = new Throwable(); + log.log(Level.SEVERE, "bar", t); + assertEquals(OsgiLogHandler.toServiceLevel(Level.SEVERE), logService.lastLevel); + assertEquals("bar", logService.lastMessage); + assertEquals(t, logService.lastThrowable); + } + + @Test + public void requireThatStadardLogLevelsAreConverted() { + assertLogLevel(LogService.LOG_ERROR, Level.SEVERE); + assertLogLevel(LogService.LOG_WARNING, Level.WARNING); + assertLogLevel(LogService.LOG_INFO, Level.INFO); + assertLogLevel(LogService.LOG_DEBUG, Level.CONFIG); + assertLogLevel(LogService.LOG_DEBUG, Level.FINE); + assertLogLevel(LogService.LOG_DEBUG, Level.FINER); + assertLogLevel(LogService.LOG_DEBUG, Level.FINEST); + } + + @Test + public void requireThatCustomLogLevelsAreConverted() { + for (int i = Level.ALL.intValue() - 69; i < Level.OFF.intValue() + 69; ++i) { + int expectedLevel; + if (i >= Level.SEVERE.intValue()) { + expectedLevel = LogService.LOG_ERROR; + } else if (i >= Level.WARNING.intValue()) { + expectedLevel = LogService.LOG_WARNING; + } else if (i >= Level.INFO.intValue()) { + expectedLevel = LogService.LOG_INFO; + } else { + expectedLevel = LogService.LOG_DEBUG; + } + assertLogLevel(expectedLevel, new MyLogLevel(i)); + } + } + + @Test + public void requireThatJdk14PropertiesAreAvailableThroughServiceReference() { + MyLogService logService = new MyLogService(); + + Logger log = newLogger(logService); + LogRecord record = new LogRecord(Level.INFO, "message"); + record.setLoggerName("loggerName"); + record.setMillis(69); + Object[] parameters = new Object[0]; + record.setParameters(parameters); + ResourceBundle resouceBundle = new MyResourceBundle(); + record.setResourceBundle(resouceBundle); + record.setResourceBundleName("resourceBundleName"); + record.setSequenceNumber(69); + record.setSourceClassName("sourceClassName"); + record.setSourceMethodName("sourceMethodName"); + record.setThreadID(69); + Throwable thrown = new Throwable(); + record.setThrown(thrown); + log.log(record); + + ServiceReference ref = logService.lastServiceReference; + assertNotNull(ref); + assertTrue(Arrays.equals(new String[] { "LEVEL", + "LOGGER_NAME", + "MESSAGE", + "MILLIS", + "PARAMETERS", + "RESOURCE_BUNDLE", + "RESOURCE_BUNDLE_NAME", + "SEQUENCE_NUMBER", + "SOURCE_CLASS_NAME", + "SOURCE_METHOD_NAME", + "THREAD_ID", + "THROWN" }, + ref.getPropertyKeys())); + assertEquals(Level.INFO, ref.getProperty("LEVEL")); + assertEquals("loggerName", ref.getProperty("LOGGER_NAME")); + assertEquals("message", ref.getProperty("MESSAGE")); + assertEquals(69L, ref.getProperty("MILLIS")); + assertSame(parameters, ref.getProperty("PARAMETERS")); + assertSame(resouceBundle, ref.getProperty("RESOURCE_BUNDLE")); + assertEquals("resourceBundleName", ref.getProperty("RESOURCE_BUNDLE_NAME")); + assertEquals(69L, ref.getProperty("SEQUENCE_NUMBER")); + assertEquals("sourceClassName", ref.getProperty("SOURCE_CLASS_NAME")); + assertEquals("sourceMethodName", ref.getProperty("SOURCE_METHOD_NAME")); + assertEquals(69, ref.getProperty("THREAD_ID")); + assertSame(thrown, ref.getProperty("THROWN")); + assertNull(ref.getProperty("unknown")); + } + + private static void assertLogLevel(int expectedLevel, Level level) { + MyLogService logService = new MyLogService(); + Logger log = newLogger(logService); + log.log(level, "message"); + assertEquals(expectedLevel, logService.lastLevel); + } + + @SuppressWarnings("unchecked") + private static Logger newLogger(LogService logService) { + Logger log = Logger.getAnonymousLogger(); + log.setUseParentHandlers(false); + log.setLevel(Level.ALL); + for (Handler handler : log.getHandlers()) { + log.removeHandler(handler); + } + log.addHandler(new OsgiLogHandler(logService)); + return log; + } + + private static class MyLogLevel extends Level { + + protected MyLogLevel(int val) { + super("foo", val); + } + } + + private static class MyLogService implements LogService { + + ServiceReference lastServiceReference; + int lastLevel; + String lastMessage; + Throwable lastThrowable; + + @Override + public void log(int level, String message) { + log(null, level, message, null); + } + + @Override + public void log(int level, String message, Throwable throwable) { + log(null, level, message, throwable); + } + + @Override + public void log(ServiceReference serviceReference, int level, String message) { + log(serviceReference, level, message, null); + } + + @Override + public void log(ServiceReference serviceReference, int level, String message, Throwable throwable) { + lastServiceReference = serviceReference; + lastLevel = level; + lastMessage = message; + lastThrowable = throwable; + } + } + + private static class MyResourceBundle extends ResourceBundle { + + @Override + protected Object handleGetObject(String key) { + return null; + } + + @Override + public Enumeration<String> getKeys() { + return null; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogManagerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogManagerTestCase.java new file mode 100644 index 00000000000..32e4b37aca8 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogManagerTestCase.java @@ -0,0 +1,184 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; +import org.mockito.Mockito; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.log.LogService; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class OsgiLogManagerTestCase { + + @Test + public void requireThatAllLogMethodsAreImplemented() throws BundleException { + FelixFramework felix = TestDriver.newOsgiFramework(); + felix.start(); + + BundleContext ctx = felix.bundleContext(); + OsgiLogManager manager = new OsgiLogManager(true); + manager.install(ctx); + MyLogService service = new MyLogService(); + ctx.registerService(LogService.class.getName(), service, null); + + manager.log(2, "a"); + assertLast(service, null, 2, "a", null); + + Throwable t1 = new Throwable(); + manager.log(4, "b", t1); + assertLast(service, null, 4, "b", t1); + + ServiceReference ref1 = Mockito.mock(ServiceReference.class); + manager.log(ref1, 8, "c"); + assertLast(service, ref1, 8, "c", null); + + ServiceReference ref2 = Mockito.mock(ServiceReference.class); + Throwable t2 = new Throwable(); + manager.log(ref2, 16, "d", t2); + assertLast(service, ref2, 16, "d", t2); + + manager.uninstall(); + felix.stop(); + } + + @Test + public void requireThatLogManagerWritesToAllRegisteredLogServices() throws BundleException { + FelixFramework felix = TestDriver.newOsgiFramework(); + felix.start(); + + BundleContext ctx = felix.bundleContext(); + MyLogService foo = new MyLogService(); + ServiceRegistration<LogService> fooReg = ctx.registerService(LogService.class, foo, null); + + OsgiLogManager manager = new OsgiLogManager(true); + manager.install(ctx); + + ServiceReference ref1 = Mockito.mock(ServiceReference.class); + Throwable t1 = new Throwable(); + manager.log(ref1, 2, "a", t1); + assertLast(foo, ref1, 2, "a", t1); + + MyLogService bar = new MyLogService(); + ServiceRegistration<LogService> barReg = ctx.registerService(LogService.class, bar, null); + + ServiceReference ref2 = Mockito.mock(ServiceReference.class); + Throwable t2 = new Throwable(); + manager.log(ref2, 4, "b", t2); + assertLast(foo, ref2, 4, "b", t2); + assertLast(bar, ref2, 4, "b", t2); + + MyLogService baz = new MyLogService(); + ServiceRegistration<LogService> bazReg = ctx.registerService(LogService.class, baz, null); + + ServiceReference ref3 = Mockito.mock(ServiceReference.class); + Throwable t3 = new Throwable(); + manager.log(ref3, 8, "c", t3); + assertLast(foo, ref3, 8, "c", t3); + assertLast(bar, ref3, 8, "c", t3); + assertLast(baz, ref3, 8, "c", t3); + + fooReg.unregister(); + + ServiceReference ref4 = Mockito.mock(ServiceReference.class); + Throwable t4 = new Throwable(); + manager.log(ref4, 16, "d", t4); + assertLast(foo, ref3, 8, "c", t3); + assertLast(bar, ref4, 16, "d", t4); + assertLast(baz, ref4, 16, "d", t4); + + barReg.unregister(); + + ServiceReference ref5 = Mockito.mock(ServiceReference.class); + Throwable t5 = new Throwable(); + manager.log(ref5, 32, "e", t5); + assertLast(foo, ref3, 8, "c", t3); + assertLast(bar, ref4, 16, "d", t4); + assertLast(baz, ref5, 32, "e", t5); + + bazReg.unregister(); + + ServiceReference ref6 = Mockito.mock(ServiceReference.class); + Throwable t6 = new Throwable(); + manager.log(ref6, 64, "f", t6); + assertLast(foo, ref3, 8, "c", t3); + assertLast(bar, ref4, 16, "d", t4); + assertLast(baz, ref5, 32, "e", t5); + + manager.uninstall(); + felix.stop(); + } + + @Test + public void requireThatRootLoggerModificationCanBeDisabled() throws BundleException { + Logger logger = Logger.getLogger(""); + logger.setLevel(Level.WARNING); + + new OsgiLogManager(false).install(Mockito.mock(BundleContext.class)); + assertEquals(Level.WARNING, logger.getLevel()); + + new OsgiLogManager(true).install(Mockito.mock(BundleContext.class)); + assertEquals(Level.ALL, logger.getLevel()); + } + + @Test + public void requireThatRootLoggerLevelIsModifiedIfNoLoggerConfigIsGiven() { + Logger logger = Logger.getLogger(""); + logger.setLevel(Level.WARNING); + + OsgiLogManager.newInstance().install(Mockito.mock(BundleContext.class)); + + assertNull(System.getProperty("java.util.logging.config.file")); + assertEquals(Level.ALL, logger.getLevel()); + } + + private static void assertLast(MyLogService service, ServiceReference ref, int level, String message, Throwable t) { + assertSame(ref, service.lastServiceReference); + assertEquals(level, service.lastLevel); + assertEquals(message, service.lastMessage); + assertSame(t, service.lastThrowable); + } + + private static class MyLogService implements LogService { + + ServiceReference lastServiceReference; + int lastLevel; + String lastMessage; + Throwable lastThrowable; + + @Override + public void log(int level, String message) { + log(null, level, message, null); + } + + @Override + public void log(int level, String message, Throwable throwable) { + log(null, level, message, throwable); + } + + @Override + public void log(ServiceReference serviceReference, int level, String message) { + log(serviceReference, level, message, null); + } + + @Override + public void log(ServiceReference serviceReference, int level, String message, Throwable throwable) { + lastServiceReference = serviceReference; + lastLevel = level; + lastMessage = message; + lastThrowable = throwable; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogServiceTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogServiceTestCase.java new file mode 100644 index 00000000000..fbd6f5a3f88 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/OsgiLogServiceTestCase.java @@ -0,0 +1,105 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.yahoo.jdisc.application.OsgiFramework; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.service.log.LogReaderService; +import org.osgi.service.log.LogService; +import org.osgi.util.tracker.ServiceTracker; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class OsgiLogServiceTestCase { + + @Test + public void requireThatLogServiceIsRegistered() throws BundleException, InterruptedException { + OsgiFramework osgi = TestDriver.newOsgiFramework(); + osgi.start(); + + ServiceTracker logs = newTracker(osgi, LogService.class); + ServiceTracker logReaders = newTracker(osgi, LogReaderService.class); + assertEquals(1, logs.getTrackingCount()); + assertEquals(1, logReaders.getTrackingCount()); + + OsgiLogService service = new OsgiLogService(); + service.start(osgi.bundleContext()); + + assertEquals(2, logs.getTrackingCount()); + assertEquals(2, logReaders.getTrackingCount()); + osgi.stop(); + } + + @Test + public void requireThatLogServiceCanNotBeStartedTwice() throws BundleException { + OsgiFramework osgi = TestDriver.newOsgiFramework(); + osgi.start(); + + BundleContext ctx = osgi.bundleContext(); + OsgiLogService service = new OsgiLogService(); + service.start(ctx); + + try { + service.start(ctx); + fail(); + } catch (IllegalStateException e) { + + } + + osgi.stop(); + } + + @Test + public void requireThatLogServiceCanNotBeStoppedTwice() throws BundleException { + OsgiFramework osgi = TestDriver.newOsgiFramework(); + osgi.start(); + + BundleContext ctx = osgi.bundleContext(); + OsgiLogService service = new OsgiLogService(); + service.start(ctx); + service.stop(); + + try { + service.stop(); + fail(); + } catch (NullPointerException e) { + + } + + osgi.stop(); + } + + @Test + public void requireThatUnstartedLogServiceCanNotBeStopped() throws BundleException { + try { + new OsgiLogService().stop(); + fail(); + } catch (NullPointerException e) { + + } + } + + @Test + public void requireThatLogServiceCanNotStartWithoutBundleContext() throws BundleException { + try { + new OsgiLogService().start(null); + fail(); + } catch (NullPointerException e) { + + } + } + + @SuppressWarnings("unchecked") + private static ServiceTracker newTracker(OsgiFramework osgi, Class trackedClass) { + ServiceTracker tracker = new ServiceTracker(osgi.bundleContext(), trackedClass, null); + tracker.open(); + return tracker; + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/ScheduledQueueTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ScheduledQueueTestCase.java new file mode 100644 index 00000000000..b208d5e5b9d --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/ScheduledQueueTestCase.java @@ -0,0 +1,149 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +import static com.yahoo.jdisc.core.ScheduledQueue.MILLIS_PER_SLOT; +import static com.yahoo.jdisc.core.ScheduledQueue.NUM_SLOTS; +import static com.yahoo.jdisc.core.ScheduledQueue.NUM_SLOTS_UNDILATED; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class ScheduledQueueTestCase { + + @Test + public void requireThatSlotMaskPreventsOverflow() { + for (int slot = 0; slot < NUM_SLOTS * 2; ++slot) { + assertTrue((slot & ScheduledQueue.SLOT_MASK) < NUM_SLOTS); + } + } + + @Test + public void requireThatIterShiftDiscardsSlotBits() { + for (int slot = 0; slot < NUM_SLOTS * 2; ++slot) { + assertEquals(slot / NUM_SLOTS, slot >> ScheduledQueue.ITER_SHIFT); + } + } + + @Test + public void requireThatNewEntryDoesNotAcceptNull() { + ScheduledQueue queue = new ScheduledQueue(0); + try { + queue.newEntry(null); + fail(); + } catch (NullPointerException e) { + + } + } + + @Test + public void requireThatEntriesCanBeScheduled() { + ScheduledQueue queue = new ScheduledQueue(0); + Object foo = new Object(); + ScheduledQueue.Entry entry = queue.newEntry(foo); + entry.scheduleAt(200); + + assertDrainTo(queue, 150); + assertDrainTo(queue, 250, foo); + } + + @Test + public void requireThatEntriesCanBeRescheduled() { + ScheduledQueue queue = new ScheduledQueue(0); + Object foo = new Object(); + ScheduledQueue.Entry entry = queue.newEntry(foo); + entry.scheduleAt(200); + entry.scheduleAt(100); + + assertDrainTo(queue, 150, foo); + assertDrainTo(queue, 250); + } + + @Test + public void requireThatEntriesCanBeUnscheduled() { + ScheduledQueue queue = new ScheduledQueue(0); + Object foo = new Object(); + ScheduledQueue.Entry entry = queue.newEntry(foo); + entry.scheduleAt(100); + entry.unschedule(); + + assertDrainTo(queue, 150); + } + + @Test + public void requireThatDrainToOnlyDrainsExpiredEntries() { + ScheduledQueue queue = new ScheduledQueue(0); + Object foo = scheduleAt(queue, 100); + Object bar = scheduleAt(queue, 300); + Object baz = scheduleAt(queue, 200); + + assertDrainTo(queue, 150, foo); + assertDrainTo(queue, 250, baz); + assertDrainTo(queue, 350, bar); + assertDrainTo(queue, 450); + } + + @Test + public void requireThatEntriesDoNotExpireMoreThanOnce() { + ScheduledQueue queue = new ScheduledQueue(0); + Object foo = scheduleAt(queue, NUM_SLOTS * MILLIS_PER_SLOT + 50); + + long now = 0; + for (int i = 0; i < NUM_SLOTS; ++i, now += MILLIS_PER_SLOT) { + assertDrainTo(queue, now); + } + assertDrainTo(queue, now += MILLIS_PER_SLOT, foo); + for (int i = 0; i < NUM_SLOTS; ++i, now += MILLIS_PER_SLOT) { + assertDrainTo(queue, now); + } + } + + @Test + public void requireThatNegativeScheduleTranslatesToNow() { + ScheduledQueue queue = new ScheduledQueue(0); + Object foo = scheduleAt(queue, -100); + + assertDrainTo(queue, 0, foo); + } + + @Test + public void requireThatDrainToPerformsTimeDilationWhenOverloaded() { + ScheduledQueue queue = new ScheduledQueue(0); + List<Object> payloads = new LinkedList<>(); + for (int i = 1; i <= NUM_SLOTS_UNDILATED + 1; ++i) { + payloads.add(scheduleAt(queue, i * MILLIS_PER_SLOT)); + } + + Queue<Object> expired = new LinkedList<>(); + long currentTimeMillis = payloads.size() * MILLIS_PER_SLOT; + queue.drainTo(currentTimeMillis, expired); + assertEquals(NUM_SLOTS_UNDILATED, expired.size()); + + expired = new LinkedList<>(); + currentTimeMillis += MILLIS_PER_SLOT; + queue.drainTo(currentTimeMillis, expired); + assertEquals(1, expired.size()); + } + + private static Object scheduleAt(ScheduledQueue queue, long expireAtMillis) { + Object obj = new Object(); + queue.newEntry(obj).scheduleAt(expireAtMillis); + return obj; + } + + private static void assertDrainTo(ScheduledQueue queue, long currentTimeMillis, Object... expected) { + Queue<Object> expired = new LinkedList<>(); + queue.drainTo(currentTimeMillis, expired); + assertEquals(expected.length, expired.size()); + assertEquals(Arrays.asList(expected), expired); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/SystemTimerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/SystemTimerTestCase.java new file mode 100644 index 00000000000..e4d0df36260 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/SystemTimerTestCase.java @@ -0,0 +1,31 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.google.inject.Guice; +import com.yahoo.jdisc.Timer; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class SystemTimerTestCase { + + @Test + public void requireThatClassIsInjectedByDefault() { + Timer timer = Guice.createInjector().getInstance(Timer.class); + assertTrue(timer instanceof SystemTimer); + } + + @Test + public void requireThatSystemTimerIsSane() { + long before = System.currentTimeMillis(); + long millis = new SystemTimer().currentTimeMillis(); + long after = System.currentTimeMillis(); + + assertTrue(before <= millis); + assertTrue(after >= millis); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/core/TimeoutManagerImplTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/core/TimeoutManagerImplTestCase.java new file mode 100644 index 00000000000..46464a9b05a --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/core/TimeoutManagerImplTestCase.java @@ -0,0 +1,579 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.core; + +import com.google.inject.Binder; +import com.google.inject.Module; +import com.yahoo.jdisc.AbstractResource; +import com.yahoo.jdisc.Container; +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.TimeoutManager; +import com.yahoo.jdisc.Timer; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.RequestDeniedException; +import com.yahoo.jdisc.handler.RequestDispatch; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.service.CurrentContainer; +import com.yahoo.jdisc.test.NonWorkingRequest; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class TimeoutManagerImplTestCase { + + private static final String REQUEST_URI = "http://host/path"; + + @Test + public void requireThatDefaultIsNoTimeout() { + Context ctx = new Context(MyRequestHandler.newEagerResponse()); + assertNull(ctx.dispatchRequest(null, MyResponseHandler.newInstance())); + assertTrue(ctx.close()); + } + + @Test + public void requireThatTimeoutCanBeSetByServerProvider() { + Context ctx = new Context(MyRequestHandler.newEagerResponse()); + assertEquals(Long.valueOf(69), ctx.dispatchRequest(69L, MyResponseHandler.newInstance())); + assertTrue(ctx.close()); + } + + @Test + public void requireThatTimeoutCanBeSetByRequestHandler() { + Context ctx = new Context(MyRequestHandler.newTimeoutWithEagerResponse(69)); + assertEquals(Long.valueOf(69), ctx.dispatchRequest(null, MyResponseHandler.newInstance())); + assertTrue(ctx.close()); + } + + @Test + public void requireThatTimeoutRequestHandlerTimeoutHasPrecedence() { + Context ctx = new Context(MyRequestHandler.newTimeoutWithEagerResponse(6)); + assertEquals(Long.valueOf(6), ctx.dispatchRequest(9L, MyResponseHandler.newInstance())); + assertTrue(ctx.close()); + } + + @Test + public void requireThatResponseCancelsTimeout() throws InterruptedException { + Context ctx = new Context(MyRequestHandler.newEagerResponse()); + assertEquals(Response.Status.OK, ctx.awaitResponse(69L, MyResponseHandler.newInstance())); + assertEquals(Response.Status.OK, ctx.awaitResponse(69L, MyResponseHandler.newInstance())); + assertTrue(ctx.close()); + } + + @Test + public void requireThatNullRequestContentCanTimeout() throws InterruptedException { + Context ctx = new Context(MyRequestHandler.newNullContent()); + assertEquals(Response.Status.REQUEST_TIMEOUT, ctx.awaitResponse(69L, MyResponseHandler.newInstance())); + assertEquals(Response.Status.REQUEST_TIMEOUT, ctx.awaitResponse(69L, MyResponseHandler.newInstance())); + assertTrue(ctx.close()); + } + + @Test + public void requireThatTimeoutWorksAfterRequestDenied() throws InterruptedException { + Context ctx = new Context(MyRequestHandler.newFirstRequestDenied()); + try { + ctx.dispatchRequest(null, MyResponseHandler.newInstance()); + fail(); + } catch (RequestDeniedException e) { + + } + assertEquals(Response.Status.REQUEST_TIMEOUT, ctx.awaitResponse(69L, MyResponseHandler.newInstance())); + assertTrue(ctx.close()); + } + + @Test + public void requireThatTimeoutWorksAfterResponseDenied() throws InterruptedException { + Context ctx = new Context(MyRequestHandler.newInstance()); + assertEquals(Response.Status.REQUEST_TIMEOUT, ctx.awaitResponse(69L, MyResponseHandler.newResponseDenied())); + assertEquals(Response.Status.REQUEST_TIMEOUT, ctx.awaitResponse(69L, MyResponseHandler.newInstance())); + assertTrue(ctx.close()); + } + + @Test + public void requireThatTimeoutWorksAfterResponseThrowsException() throws InterruptedException { + Context ctx = new Context(MyRequestHandler.newInstance()); + assertEquals(Response.Status.REQUEST_TIMEOUT, ctx.awaitResponse(69L, MyResponseHandler.newThrowException())); + assertEquals(Response.Status.REQUEST_TIMEOUT, ctx.awaitResponse(69L, MyResponseHandler.newInstance())); + assertTrue(ctx.close()); + } + + @Test + public void requireThatTimeoutWorksAfterResponseInterruptsThread() throws InterruptedException { + Context ctx = new Context(MyRequestHandler.newInstance()); + assertEquals(Response.Status.REQUEST_TIMEOUT, ctx.awaitResponse(69L, MyResponseHandler.newInterruptThread())); + assertEquals(Response.Status.REQUEST_TIMEOUT, ctx.awaitResponse(69L, MyResponseHandler.newInstance())); + assertTrue(ctx.close()); + } + + @Test + public void requireThatTimeoutOccursInOrder() throws InterruptedException { + Context ctx = new Context(MyRequestHandler.newInstance()); + MyResponseHandler foo = MyResponseHandler.newInstance(); + ctx.dispatchRequest(300L, foo); + + MyResponseHandler bar = MyResponseHandler.newInstance(); + ctx.dispatchRequest(100L, bar); + + MyResponseHandler baz = MyResponseHandler.newInstance(); + ctx.dispatchRequest(200L, baz); + + ctx.forwardToTime(100); + assertFalse(foo.await(10, TimeUnit.MILLISECONDS)); + assertTrue(bar.await(600, TimeUnit.SECONDS)); + assertFalse(baz.await(10, TimeUnit.MILLISECONDS)); + + ctx.forwardToTime(200); + assertFalse(foo.await(10, TimeUnit.MILLISECONDS)); + assertTrue(baz.await(600, TimeUnit.SECONDS)); + + ctx.forwardToTime(300); + assertTrue(foo.await(600, TimeUnit.SECONDS)); + + assertTrue(ctx.close()); + } + + @Test + public void requireThatResponseHandlerIsWellBehavedAfterTimeout() throws InterruptedException { + Context ctx = new Context(MyRequestHandler.newInstance()); + assertEquals(Response.Status.REQUEST_TIMEOUT, ctx.awaitResponse(69L, MyResponseHandler.newInstance())); + + ContentChannel content = ctx.requestHandler.responseHandler.handleResponse(new Response(Response.Status.OK)); + assertNotNull(content); + + content.write(ByteBuffer.allocate(69), null); + MyCompletion completion = new MyCompletion(); + content.write(ByteBuffer.allocate(69), completion); + assertTrue(completion.completed.await(600, TimeUnit.SECONDS)); + + completion = new MyCompletion(); + content.close(completion); + assertTrue(completion.completed.await(600, TimeUnit.SECONDS)); + + assertTrue(ctx.close()); + } + + @Test + public void requireThatManagedHandlerForwardsAllCalls() throws InterruptedException { + Request request = NonWorkingRequest.newInstance(REQUEST_URI); + MyRequestHandler requestHandler = MyRequestHandler.newInstance(); + TimeoutManagerImpl timeoutManager = new TimeoutManagerImpl(Executors.defaultThreadFactory(), + new SystemTimer()); + RequestHandler managedHandler = timeoutManager.manageHandler(requestHandler); + + MyResponseHandler responseHandler = MyResponseHandler.newInstance(); + ContentChannel requestContent = managedHandler.handleRequest(request, responseHandler); + assertNotNull(requestContent); + + ByteBuffer buf = ByteBuffer.allocate(69); + requestContent.write(buf, null); + assertSame(buf, requestHandler.content.buf); + MyCompletion writeCompletion = new MyCompletion(); + requestContent.write(buf = ByteBuffer.allocate(69), writeCompletion); + assertSame(buf, requestHandler.content.buf); + requestHandler.content.writeCompletion.completed(); + assertTrue(writeCompletion.completed.await(600, TimeUnit.SECONDS)); + + MyCompletion closeCompletion = new MyCompletion(); + requestContent.close(closeCompletion); + requestHandler.content.closeCompletion.completed(); + assertTrue(closeCompletion.completed.await(600, TimeUnit.SECONDS)); + + managedHandler.release(); + assertTrue(requestHandler.destroyed); + + Response response = new Response(Response.Status.OK); + ContentChannel responseContent = requestHandler.responseHandler.handleResponse(response); + assertNotNull(responseContent); + + responseContent.write(buf = ByteBuffer.allocate(69), null); + assertSame(buf, responseHandler.content.buf); + responseContent.write(buf = ByteBuffer.allocate(69), writeCompletion = new MyCompletion()); + assertSame(buf, responseHandler.content.buf); + responseHandler.content.writeCompletion.completed(); + assertTrue(writeCompletion.completed.await(600, TimeUnit.SECONDS)); + + responseContent.close(closeCompletion = new MyCompletion()); + responseHandler.content.closeCompletion.completed(); + assertTrue(closeCompletion.completed.await(600, TimeUnit.SECONDS)); + + assertSame(response, responseHandler.response.get()); + } + + @Test + public void requireThatTimeoutOccursAtExpectedTime() throws InterruptedException { + final Context ctx = new Context(MyRequestHandler.newInstance()); + final MyResponseHandler responseHandler = MyResponseHandler.newInstance(); + + ctx.forwardToTime(100); + new RequestDispatch() { + + @Override + protected Request newRequest() { + Request request = new Request(ctx.driver, URI.create(REQUEST_URI)); + request.setTimeout(300, TimeUnit.MILLISECONDS); + return request; + } + + @Override + public ContentChannel handleResponse(Response response) { + return responseHandler.handleResponse(response); + } + }.dispatch(); + + ctx.forwardToTime(300); + assertFalse(responseHandler.await(100, TimeUnit.MILLISECONDS)); + ctx.forwardToTime(400); + assertTrue(responseHandler.await(600, TimeUnit.SECONDS)); + + Response response = responseHandler.response.get(); + assertNotNull(response); + assertEquals(Response.Status.REQUEST_TIMEOUT, response.getStatus()); + assertTrue(ctx.close()); + } + + @Test + public void requireThatQueueEntryIsRemovedWhenResponseHandlerIsCalledBeforeTimeout() { + Context ctx = new Context(MyRequestHandler.newInstance()); + ctx.dispatchRequest(69L, MyResponseHandler.newInstance()); + assertTrue(ctx.awaitQueueSize(1, 600, TimeUnit.SECONDS)); + ctx.requestHandler.respond(); + assertTrue(ctx.awaitQueueSize(0, 600, TimeUnit.SECONDS)); + assertTrue(ctx.close()); + } + + @Test + public void requireThatNoEntryIsMadeIfTimeoutIsNull() { + Context ctx = new Context(MyRequestHandler.newInstance()); + ctx.dispatchRequest(null, MyResponseHandler.newInstance()); + assertFalse(ctx.awaitQueueSize(1, 100, TimeUnit.MILLISECONDS)); + assertTrue(ctx.awaitQueueSize(0, 600, TimeUnit.SECONDS)); + ctx.requestHandler.respond(); + assertTrue(ctx.close()); + } + + @Test + public void requireThatNoEntryIsMadeIfHandleRequestCallsHandleResponse() { + Context ctx = new Context(MyRequestHandler.newEagerResponse()); + ctx.dispatchRequest(69L, MyResponseHandler.newInstance()); + assertFalse(ctx.awaitQueueSize(1, 100, TimeUnit.MILLISECONDS)); + assertTrue(ctx.awaitQueueSize(0, 600, TimeUnit.SECONDS)); + assertTrue(ctx.close()); + } + + @Test + public void requireThatNoEntryIsMadeIfTimeoutHandlerHasBeenSet() { + final Context ctx = new Context(MyRequestHandler.newInstance()); + new RequestDispatch() { + + @Override + protected Request newRequest() { + Request request = new Request(ctx.driver, URI.create(REQUEST_URI)); + request.setTimeout(10, TimeUnit.MILLISECONDS); + request.setTimeoutManager(new TimeoutManager() { + + @Override + public void scheduleTimeout(Request request) { + + } + }); + return request; + } + }.dispatch(); + + assertFalse(ctx.awaitQueueSize(1, 100, TimeUnit.MILLISECONDS)); + assertTrue(ctx.awaitQueueSize(0, 600, TimeUnit.SECONDS)); + ctx.requestHandler.respond(); + assertTrue(ctx.close()); + } + + private static class Context implements Module, Timer { + + final MyRequestHandler requestHandler; + final TimeoutManagerImpl timeoutManager; + final TestDriver driver; + long millis = 0; + + Context(MyRequestHandler requestHandler) { + this.requestHandler = requestHandler; + this.driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(this); + + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind(REQUEST_URI, requestHandler); + driver.activateContainer(builder); + + Container ref = driver.newReference(URI.create(REQUEST_URI)); + timeoutManager = ref.getInstance(TimeoutManagerImpl.class); + ref.release(); + } + + void forwardToTime(long millis) { + while (this.millis < millis) { + this.millis += ScheduledQueue.MILLIS_PER_SLOT; + timeoutManager.checkTasks(this.millis); + } + } + + boolean close() { + return driver.close(); + } + + @Override + public void configure(Binder binder) { + binder.bind(Timer.class).toInstance(this); + } + + @Override + public long currentTimeMillis() { + return millis; + } + + int awaitResponse(Long serverProviderTimeout, MyResponseHandler responseHandler) throws InterruptedException { + Long timeout = new MyServerProvider(serverProviderTimeout).dispatchRequest(driver, responseHandler); + long timeoutAt; + if (timeout == null) { + timeoutAt = millis + TimeUnit.SECONDS.toMillis(120); + } else { + timeoutAt = millis + timeout; + } + forwardToTime(timeoutAt); + if (!responseHandler.await(600, TimeUnit.SECONDS)) { + fail("Request handler failed to respond within allocated time."); + } + return responseHandler.response.get().getStatus(); + } + + boolean awaitQueueSize(int expectedSize, int timeout, TimeUnit unit) { + for (long i = 0, len = unit.toMillis(timeout) / 100; i < len; ++i) { + if (timeoutManager.queueSize() == expectedSize) { + return true; + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + fail(); + } + } + return false; + } + + public Long dispatchRequest(Long serverProviderTimeout, MyResponseHandler responseHandler) { + return new MyServerProvider(serverProviderTimeout).dispatchRequest(driver, responseHandler); + } + } + + private static class MyServerProvider { + + final Long timeout; + + MyServerProvider(Long timeout) { + this.timeout = timeout; + } + + Long dispatchRequest(CurrentContainer container, ResponseHandler responseHandler) { + Request request = null; + ContentChannel content = null; + try { + request = new Request(container, URI.create(REQUEST_URI)); + if (timeout != null) { + request.setTimeout(timeout, TimeUnit.MILLISECONDS); + } + content = request.connect(responseHandler); + } finally { + if (request != null) { + request.release(); + } + if (content != null) { + content.close(null); + } + } + return request.getTimeout(TimeUnit.MILLISECONDS); + } + } + + private static class MyCompletion implements CompletionHandler { + + final CountDownLatch completed = new CountDownLatch(1); + + @Override + public void completed() { + completed.countDown(); + } + + @Override + public void failed(Throwable t) { + + } + } + + private static class MyContent implements ContentChannel { + + ByteBuffer buf; + CompletionHandler writeCompletion; + CompletionHandler closeCompletion; + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + this.buf = buf; + this.writeCompletion = handler; + if (handler != null) { + handler.completed(); + } + } + + @Override + public void close(CompletionHandler handler) { + this.closeCompletion = handler; + if (handler != null) { + handler.completed(); + } + } + + static MyContent newInstance() { + return new MyContent(); + } + } + + private static class MyResponseHandler implements ResponseHandler { + + final AtomicReference<CountDownLatch> latch = new AtomicReference<>(new CountDownLatch(1)); + final AtomicReference<Response> response = new AtomicReference<>(); + final MyContent content; + final boolean throwException; + final boolean interruptThread; + + MyResponseHandler(MyContent content, boolean throwException, boolean interruptThread) { + this.content = content; + this.throwException = throwException; + this.interruptThread = interruptThread; + } + + boolean await(long timeout, TimeUnit unit) throws InterruptedException { + return latch.get().await(timeout, unit); + } + + @Override + public ContentChannel handleResponse(Response response) { + if (this.response.getAndSet(response) != null) { + throw new IllegalStateException("Response already received."); + } + latch.get().countDown(); + if (interruptThread) { + Thread.currentThread().interrupt(); + } + if (throwException) { + throw new MyException(); + } + return content; + } + + static MyResponseHandler newInstance() { + return new MyResponseHandler(MyContent.newInstance(), false, false); + } + + static MyResponseHandler newResponseDenied() { + return new MyResponseHandler(null, false, false); + } + + static MyResponseHandler newThrowException() { + return new MyResponseHandler(null, true, false); + } + + static MyResponseHandler newInterruptThread() { + return new MyResponseHandler(MyContent.newInstance(), false, true); + } + } + + private static class MyRequestHandler extends AbstractResource implements RequestHandler { + + final MyContent content; + final Long timeout; + int numDenied; + int numEager; + Request request = null; + ResponseHandler responseHandler = null; + boolean destroyed = false; + + MyRequestHandler(int numDenied, MyContent content, Long timeout, int numEager) { + this.numDenied = numDenied; + this.content = content; + this.timeout = timeout; + this.numEager = numEager; + } + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + if (--numDenied >= 0) { + throw new RequestDeniedException(request); + } + this.request = request; + this.responseHandler = handler; + if (timeout != null) { + request.setTimeout(timeout, TimeUnit.MILLISECONDS); + } + if (--numEager >= 0) { + respond(); + } + return content; + } + + @Override + public void handleTimeout(Request request, ResponseHandler handler) { + Response.dispatchTimeout(handler); + } + + @Override + protected void destroy() { + destroyed = true; + } + + void respond() { + ContentChannel content = responseHandler.handleResponse(new Response(Response.Status.OK)); + if (content != null) { + content.close(null); + } + } + + static MyRequestHandler newInstance() { + return new MyRequestHandler(0, MyContent.newInstance(), null, 0); + } + + static MyRequestHandler newTimeoutWithEagerResponse(long millis) { + return new MyRequestHandler(0, MyContent.newInstance(), millis, Integer.MAX_VALUE); + } + + static MyRequestHandler newFirstRequestDenied() { + return new MyRequestHandler(1, MyContent.newInstance(), null, 0); + } + + static MyRequestHandler newEagerResponse() { + return new MyRequestHandler(0, MyContent.newInstance(), null, Integer.MAX_VALUE); + } + + public static MyRequestHandler newNullContent() { + return new MyRequestHandler(0, null, null, 0); + } + } + + private static class MyException extends RuntimeException { + + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractContentOutputStreamTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractContentOutputStreamTestCase.java new file mode 100644 index 00000000000..d314b303bd4 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractContentOutputStreamTestCase.java @@ -0,0 +1,127 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class AbstractContentOutputStreamTestCase { + + @Test + public void requireThatStreamCanBeWrittenTo() throws IOException { + MyOutputStream out = new MyOutputStream(); + int len = 2 * AbstractContentOutputStream.BUFFERSIZE; + for (int i = 0; i < len; ++i) { + out.write(69); + out.write(new byte[] { }); + out.write(new byte[] { 6, 9 }); + out.write(new byte[] { 6, 69, 9 }, 1, 0); // zero length + out.write(new byte[] { 6, 69, 9 }, 1, 1); + } + out.close(); + + InputStream in = out.toInputStream(); + for (int i = 0; i < len; ++i) { + assertEquals(69, in.read()); + assertEquals(6, in.read()); + assertEquals(9, in.read()); + assertEquals(69, in.read()); + } + assertEquals(-1, in.read()); + assertTrue(out.closed); + } + + @Test + public void requireThatBigBuffersAreWrittenInOrder() throws IOException { + MyOutputStream out = new MyOutputStream(); + out.write(6); + out.write(new byte[2 * AbstractContentOutputStream.BUFFERSIZE]); + out.write(9); + out.close(); + InputStream in = out.toInputStream(); + assertEquals(6, in.read()); + for (int i = 0, len = 2 * AbstractContentOutputStream.BUFFERSIZE; i < len; ++i) { + assertEquals(0, in.read()); + } + assertEquals(9, in.read()); + assertEquals(-1, in.read()); + assertTrue(out.closed); + } + + @Test + public void requireThatEmptyBuffersAreNotFlushed() throws Exception { + MyOutputStream out = new MyOutputStream(); + out.close(); + assertTrue(out.writes.isEmpty()); + assertTrue(out.closed); + } + + @Test + public void requireThatNoExcessiveBytesAreWritten() throws Exception { + MyOutputStream out = new MyOutputStream(); + out.write(new byte[] { 6, 9 }); + out.close(); + + InputStream in = out.toInputStream(); + assertEquals(2, in.available()); + assertEquals(6, in.read()); + assertEquals(9, in.read()); + assertEquals(0, in.available()); + assertEquals(-1, in.read()); + assertTrue(out.closed); + } + + @Test + public void requireThatWrittenArraysAreCopied() throws Exception { + MyOutputStream out = new MyOutputStream(); + byte[] buf = new byte[1]; + for (byte b = 0; b < 127; ++b) { + buf[0] = b; + out.write(buf); + } + out.close(); + + InputStream in = out.toInputStream(); + for (byte b = 0; b < 127; ++b) { + assertEquals(b, in.read()); + } + } + + private static class MyOutputStream extends AbstractContentOutputStream { + + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final List<ByteBuffer> writes = new ArrayList<>(); + boolean closed; + + @Override + protected void doFlush(ByteBuffer buf) { + writes.add(buf); + buf = buf.slice(); + while (buf.hasRemaining()) { + out.write(buf.get()); + } + } + + @Override + protected void doClose() { + closed = true; + } + + InputStream toInputStream() { + return new ByteArrayInputStream(out.toByteArray()); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractRequestHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractRequestHandlerTestCase.java new file mode 100644 index 00000000000..661165ac5e8 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/AbstractRequestHandlerTestCase.java @@ -0,0 +1,187 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.test.NonWorkingRequest; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class AbstractRequestHandlerTestCase { + + private static final Charset UTF8 = Charset.forName("utf-8"); + private static int NUM_REQUESTS = 666; + + @Test + public void requireThatHandleTimeoutIsImplemented() throws Exception { + FutureResponse handler = new FutureResponse(); + new AbstractRequestHandler() { + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + return null; + } + }.handleTimeout(NonWorkingRequest.newInstance("http://localhost/"), handler); + Response response = handler.get(600, TimeUnit.SECONDS); + assertNotNull(response); + assertEquals(Response.Status.REQUEST_TIMEOUT, response.getStatus()); + } + + @Test + public void requireThatHelloWorldWorks() throws InterruptedException { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind("http://localhost/", new HelloWorldHandler()); + driver.activateContainer(builder); + + for (int i = 0; i < NUM_REQUESTS; ++i) { + MyResponseHandler responseHandler = new MyResponseHandler(); + driver.newRequestDispatch("http://localhost/", responseHandler).dispatch(); + + ByteBuffer buf = responseHandler.content.read(); + assertNotNull(buf); + assertEquals("Hello World!", new String(buf.array(), buf.arrayOffset() + buf.position(), buf.remaining(), UTF8)); + assertNull(responseHandler.content.read()); + } + assertTrue(driver.close()); + } + + @Test + public void requireThatEchoWorks() throws InterruptedException { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind("http://localhost/", new EchoHandler()); + driver.activateContainer(builder); + + for (int i = 0; i < NUM_REQUESTS; ++i) { + MyResponseHandler responseHandler = new MyResponseHandler(); + RequestDispatch dispatch = driver.newRequestDispatch("http://localhost/", responseHandler); + FastContentWriter requestContent = dispatch.connectFastWriter(); + ByteBuffer buf = ByteBuffer.allocate(69); + requestContent.write(buf); + requestContent.close(); + + assertSame(buf, responseHandler.content.read()); + assertNull(responseHandler.content.read()); + } + assertTrue(driver.close()); + } + + @Test + public void requireThatForwardWorks() throws InterruptedException { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind("http://localhost/", new ForwardHandler()); + builder.clientBindings().bind("http://remotehost/", new EchoHandler()); + driver.activateContainer(builder); + + for (int i = 0; i < NUM_REQUESTS; ++i) { + MyResponseHandler responseHandler = new MyResponseHandler(); + RequestDispatch dispatch = driver.newRequestDispatch("http://localhost/", responseHandler); + FastContentWriter requestContent = dispatch.connectFastWriter(); + ByteBuffer buf = ByteBuffer.allocate(69); + requestContent.write(buf); + requestContent.close(); + + assertSame(buf, responseHandler.content.read()); + assertNull(responseHandler.content.read()); + } + assertTrue(driver.close()); + } + + private static class HelloWorldHandler extends AbstractRequestHandler { + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + FastContentWriter writer = ResponseDispatch.newInstance(Response.Status.OK).connectFastWriter(handler); + try { + writer.write("Hello World!"); + } finally { + writer.close(); + } + return null; + } + } + + private static class EchoHandler extends AbstractRequestHandler { + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + return new WritingContentChannel(new FastContentWriter(ResponseDispatch.newInstance(Response.Status.OK).connect(handler))); + } + } + + private static class ForwardHandler extends AbstractRequestHandler { + + @Override + public ContentChannel handleRequest(final Request request, final ResponseHandler handler) { + return new WritingContentChannel(new FastContentWriter(new RequestDispatch() { + + @Override + public Request newRequest() { + return new Request(request, URI.create("http://remotehost/")); + } + + @Override + public ContentChannel handleResponse(Response response) { + return handler.handleResponse(response); + } + }.connect())); + } + } + + private static class WritingContentChannel implements ContentChannel { + + final FastContentWriter writer; + + WritingContentChannel(FastContentWriter writer) { + this.writer = writer; + } + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + try { + writer.write(buf); + handler.completed(); + } catch (Exception e) { + handler.failed(e); + } + } + + @Override + public void close(CompletionHandler handler) { + try { + writer.close(); + handler.completed(); + } catch (Exception e) { + handler.failed(e); + } + } + } + + private static class MyResponseHandler implements ResponseHandler { + + final ReadableContentChannel content = new ReadableContentChannel(); + + @Override + public ContentChannel handleResponse(Response response) { + return content; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BindingNotFoundTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BindingNotFoundTestCase.java new file mode 100644 index 00000000000..58900174ee2 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BindingNotFoundTestCase.java @@ -0,0 +1,49 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class BindingNotFoundTestCase { + + @Test + public void requireThatAccessorsWork() { + URI uri = URI.create("http://host/path"); + BindingNotFoundException e = new BindingNotFoundException(uri); + assertEquals(uri, e.uri()); + } + + @Test + public void requireThatBindingNotFoundIsThrown() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + driver.activateContainer(driver.newContainerBuilder()); + Request request = new Request(driver, URI.create("http://host/path")); + try { + request.connect(new MyResponseHandler()); + fail(); + } catch (BindingNotFoundException e) { + assertEquals(request.getUri(), e.uri()); + } + request.release(); + driver.close(); + } + + private class MyResponseHandler implements ResponseHandler { + + @Override + public ContentChannel handleResponse(Response response) { + return null; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BlockingContentWriterTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BlockingContentWriterTestCase.java new file mode 100644 index 00000000000..b539c135edb --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BlockingContentWriterTestCase.java @@ -0,0 +1,210 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.util.concurrent.*; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class BlockingContentWriterTestCase { + + @Test + public void requireThatContentChannelIsNotNull() { + try { + new BlockingContentWriter(null); + fail(); + } catch (NullPointerException e) { + + } + } + + @Test + public void requireThatWriteDeliversBuffer() throws InterruptedException { + MyContent content = MyContent.newNonBlockingContent(); + BlockingContentWriter writer = new BlockingContentWriter(content); + ByteBuffer buf = ByteBuffer.allocate(69); + writer.write(buf); + assertSame(buf, content.writeBuf); + } + + @Test + public void requireThatWriteIsBlocking() throws Exception { + MyContent content = MyContent.newBlockingContent(); + BlockingContentWriter writer = new BlockingContentWriter(content); + FutureTask<Boolean> task = new FutureTask<>(new WriteTask(writer, ByteBuffer.allocate(69))); + Executors.newSingleThreadExecutor().submit(task); + content.writeLatch.await(600, TimeUnit.SECONDS); + try { + task.get(100, TimeUnit.MILLISECONDS); + fail(); + } catch (TimeoutException e) { + + } + content.writeCompletion.completed(); + assertTrue(task.get(600, TimeUnit.SECONDS)); + } + + @Test + public void requireThatWriteExceptionIsThrown() throws Exception { + Throwable throwMe = new RuntimeException(); + try { + new BlockingContentWriter(MyContent.newFailedContent(throwMe)).write(ByteBuffer.allocate(69)); + } catch (Throwable t) { + assertSame(throwMe, t); + } + throwMe = new Error(); + try { + new BlockingContentWriter(MyContent.newFailedContent(throwMe)).write(ByteBuffer.allocate(69)); + } catch (Throwable t) { + assertSame(throwMe, t); + } + throwMe = new Exception(); + try { + new BlockingContentWriter(MyContent.newFailedContent(throwMe)).write(ByteBuffer.allocate(69)); + } catch (Throwable t) { + assertNotSame(throwMe, t); + assertSame(throwMe, t.getCause()); + } + } + + @Test + public void requireThatCloseIsBlocking() throws Exception { + MyContent content = MyContent.newBlockingContent(); + BlockingContentWriter writer = new BlockingContentWriter(content); + FutureTask<Boolean> task = new FutureTask<>(new CloseTask(writer)); + Executors.newSingleThreadExecutor().submit(task); + content.closeLatch.await(600, TimeUnit.SECONDS); + try { + task.get(100, TimeUnit.MILLISECONDS); + fail(); + } catch (TimeoutException e) { + + } + content.closeCompletion.completed(); + assertTrue(task.get(600, TimeUnit.SECONDS)); + } + + @Test + public void requireThatCloseExceptionIsThrown() throws Exception { + Throwable throwMe = new RuntimeException(); + try { + new BlockingContentWriter(MyContent.newFailedContent(throwMe)).close(); + } catch (Throwable t) { + assertSame(throwMe, t); + } + throwMe = new Error(); + try { + new BlockingContentWriter(MyContent.newFailedContent(throwMe)).close(); + } catch (Throwable t) { + assertSame(throwMe, t); + } + throwMe = new Exception(); + try { + new BlockingContentWriter(MyContent.newFailedContent(throwMe)).close(); + } catch (Throwable t) { + assertNotSame(throwMe, t); + assertSame(throwMe, t.getCause()); + } + } + + private static class MyContent implements ContentChannel { + + final CountDownLatch writeLatch = new CountDownLatch(1); + final CountDownLatch closeLatch = new CountDownLatch(1); + final Throwable eagerFailure; + final boolean eagerCompletion; + CompletionHandler writeCompletion; + CompletionHandler closeCompletion; + ByteBuffer writeBuf; + + MyContent(boolean eagerCompletion, Throwable eagerFailure) { + this.eagerCompletion = eagerCompletion; + this.eagerFailure = eagerFailure; + } + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + writeBuf = buf; + if (eagerFailure != null) { + handler.failed(eagerFailure); + } else if (eagerCompletion) { + handler.completed(); + } else { + writeCompletion = handler; + } + writeLatch.countDown(); + } + + @Override + public void close(CompletionHandler handler) { + if (eagerFailure != null) { + handler.failed(eagerFailure); + } else if (eagerCompletion) { + handler.completed(); + } else { + closeCompletion = handler; + } + closeLatch.countDown(); + } + + static MyContent newBlockingContent() { + return new MyContent(false, null); + } + + static MyContent newNonBlockingContent() { + return new MyContent(true, null); + } + + static MyContent newFailedContent(Throwable e) { + return new MyContent(false, e); + } + } + + private static class WriteTask implements Callable<Boolean> { + + final BlockingContentWriter writer; + final ByteBuffer buf; + + WriteTask(BlockingContentWriter writer, ByteBuffer buf) { + this.writer = writer; + this.buf = buf; + } + + @Override + public Boolean call() { + try { + writer.write(buf); + } catch (Throwable t) { + return false; + } + return true; + } + } + + private static class CloseTask implements Callable<Boolean> { + + final BlockingContentWriter writer; + + CloseTask(BlockingContentWriter writer) { + this.writer = writer; + } + + @Override + public Boolean call() { + try { + writer.close(); + } catch (Throwable t) { + return false; + } + return true; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BufferedContentChannelTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BufferedContentChannelTestCase.java new file mode 100644 index 00000000000..c6714f11203 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/BufferedContentChannelTestCase.java @@ -0,0 +1,257 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.*; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class BufferedContentChannelTestCase { + + @Test + public void requireThatIsConnectedWorks() { + MyContent target = new MyContent(); + BufferedContentChannel content = new BufferedContentChannel(); + assertFalse(content.isConnected()); + content.connectTo(target); + assertTrue(content.isConnected()); + } + + @Test + public void requireThatConnectToNullThrowsException() { + BufferedContentChannel content = new BufferedContentChannel(); + try { + content.connectTo(null); + fail(); + } catch (NullPointerException e) { + + } + } + + @Test + public void requireThatWriteAfterCloseThrowsException() { + BufferedContentChannel content = new BufferedContentChannel(); + content.close(null); + try { + content.write(ByteBuffer.allocate(69), null); + fail(); + } catch (IllegalStateException e) { + + } + } + + @Test + public void requireThatCloseAfterCloseThrowsException() { + BufferedContentChannel content = new BufferedContentChannel(); + content.close(null); + try { + content.close(null); + fail(); + } catch (IllegalStateException e) { + + } + } + + @Test + public void requireThatConnecToAfterConnecToThrowsException() { + BufferedContentChannel content = new BufferedContentChannel(); + content.connectTo(new MyContent()); + try { + content.connectTo(new MyContent()); + fail(); + } catch (IllegalStateException e) { + + } + } + + @Test + public void requireThatWriteBeforeConnectToWritesToTarget() { + BufferedContentChannel content = new BufferedContentChannel(); + ByteBuffer buf = ByteBuffer.allocate(69); + MyCompletion completion = new MyCompletion(); + content.write(buf, completion); + MyContent target = new MyContent(); + content.connectTo(target); + assertSame(buf, target.writeBuf); + assertSame(completion, target.writeCompletion); + } + + @Test + public void requireThatWriteAfterConnectToWritesToTarget() { + MyContent target = new MyContent(); + BufferedContentChannel content = new BufferedContentChannel(); + content.connectTo(target); + ByteBuffer buf = ByteBuffer.allocate(69); + MyCompletion completion = new MyCompletion(); + content.write(buf, completion); + assertSame(buf, target.writeBuf); + assertSame(completion, target.writeCompletion); + } + + @Test + public void requireThatCloseBeforeConnectToClosesTarget() { + BufferedContentChannel content = new BufferedContentChannel(); + MyCompletion completion = new MyCompletion(); + content.close(completion); + MyContent target = new MyContent(); + content.connectTo(target); + assertTrue(target.closed); + assertSame(completion, target.closeCompletion); + } + + @Test + public void requireThatCloseAfterConnectToClosesTarget() { + MyContent target = new MyContent(); + BufferedContentChannel content = new BufferedContentChannel(); + content.connectTo(target); + MyCompletion completion = new MyCompletion(); + content.close(completion); + assertTrue(target.closed); + assertSame(completion, target.closeCompletion); + } + + @Test + public void requireThatIsConnectedIsTrueWhenConnectedBeforeClose() { + BufferedContentChannel content = new BufferedContentChannel(); + assertFalse(content.isConnected()); + content.connectTo(new MyContent()); + assertTrue(content.isConnected()); + content.close(null); + assertTrue(content.isConnected()); + } + + @Test + public void requireThatIsConnectedIsTrueWhenClosedBeforeConnected() { + BufferedContentChannel content = new BufferedContentChannel(); + assertFalse(content.isConnected()); + content.close(null); + assertFalse(content.isConnected()); + content.connectTo(new MyContent()); + assertTrue(content.isConnected()); + } + + @Test + public void requireThatContentIsThreadSafe() throws Exception { + ExecutorService executor = Executors.newFixedThreadPool(101); + for (int run = 0; run < 69; ++run) { + List<ByteBuffer> bufs = new LinkedList<>(); + for (int buf = 0; buf < 100; ++buf) { + bufs.add(ByteBuffer.allocate(buf)); + } + BufferedContentChannel content = new BufferedContentChannel(); + List<Callable<Boolean>> tasks = new LinkedList<>(); + for (ByteBuffer buf : bufs) { + tasks.add(new WriteTask(content, buf)); + } + MyConcurrentContent target = new MyConcurrentContent(); + tasks.add(new ConnectTask(content, target)); + List<Future<Boolean>> results = executor.invokeAll(tasks); + for (Future<Boolean> result : results) { + assertTrue(result.get()); + } + assertEquals(bufs.size(), target.bufs.size()); + for (ByteBuffer buf : target.bufs) { + assertTrue(bufs.remove(buf)); + } + assertTrue(bufs.isEmpty()); + } + } + + private static class WriteTask implements Callable<Boolean> { + + final Random rnd = new Random(); + final BufferedContentChannel content; + final ByteBuffer buf; + + WriteTask(BufferedContentChannel content, ByteBuffer buf) { + this.content = content; + this.buf = buf; + } + + @Override + public Boolean call() throws Exception { + if (rnd.nextBoolean()) { + Thread.sleep(rnd.nextInt(5)); + } + content.write(buf, null); + return Boolean.TRUE; + } + } + + private static class ConnectTask implements Callable<Boolean> { + + final BufferedContentChannel content; + final ContentChannel target; + + ConnectTask(BufferedContentChannel content, ContentChannel target) { + this.content = content; + this.target = target; + } + + @Override + public Boolean call() throws Exception { + content.connectTo(target); + return Boolean.TRUE; + } + } + + private static class MyContent implements ContentChannel { + + ByteBuffer writeBuf = null; + CompletionHandler writeCompletion; + CompletionHandler closeCompletion; + boolean closed = false; + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + writeBuf = buf; + writeCompletion = handler; + } + + @Override + public void close(CompletionHandler handler) { + closeCompletion = handler; + closed = true; + } + } + + private static class MyConcurrentContent implements ContentChannel { + + ConcurrentLinkedQueue<ByteBuffer> bufs = new ConcurrentLinkedQueue<>(); + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + bufs.add(buf); + } + + @Override + public void close(CompletionHandler handler) { + + } + } + + private static class MyCompletion implements CompletionHandler { + + @Override + public void completed() { + + } + + @Override + public void failed(Throwable throwable) { + + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableRequestDispatchTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableRequestDispatchTestCase.java new file mode 100644 index 00000000000..d2768707528 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableRequestDispatchTestCase.java @@ -0,0 +1,51 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; + +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class CallableRequestDispatchTestCase { + + @Test + public void requireThatDispatchIsCalled() throws Exception { + final TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + Response response = new Response(Response.Status.OK); + builder.serverBindings().bind("http://host/path", new MyRequestHandler(response)); + driver.activateContainer(builder); + assertSame(response, new CallableRequestDispatch() { + + @Override + protected Request newRequest() { + return new Request(driver, URI.create("http://host/path")); + } + }.call()); + assertTrue(driver.close()); + } + + private static class MyRequestHandler extends AbstractRequestHandler { + + final Response response; + + MyRequestHandler(Response response) { + this.response = response; + } + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + ResponseDispatch.newInstance(response).dispatch(handler); + return null; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableResponseDispatchTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableResponseDispatchTestCase.java new file mode 100644 index 00000000000..9b107f93178 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/CallableResponseDispatchTestCase.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.jdisc.handler; + +import com.yahoo.jdisc.Response; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertSame; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class CallableResponseDispatchTestCase { + + @Test + public void requireThatDispatchIsCalled() throws Exception { + final Response response = new Response(Response.Status.OK); + FutureResponse handler = new FutureResponse(); + new CallableResponseDispatch(handler) { + + @Override + protected Response newResponse() { + return response; + } + }.call(); + assertSame(response, handler.get(600, TimeUnit.SECONDS)); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ContentInputStreamTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ContentInputStreamTestCase.java new file mode 100644 index 00000000000..618c1b1ed1c --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ContentInputStreamTestCase.java @@ -0,0 +1,32 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import org.junit.Test; + +import java.util.concurrent.Future; + +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ContentInputStreamTestCase { + + @Test + public void requireThatContentInputStreamExtendsUnsafeContentInputStream() { + assertTrue(UnsafeContentInputStream.class.isAssignableFrom(ContentInputStream.class)); + } + + @Test + @SuppressWarnings("FinalizeCalledExplicitly") + public void requireThatFinalizerClosesStream() throws Throwable { + BufferedContentChannel channel = new BufferedContentChannel(); + FastContentWriter writer = new FastContentWriter(channel); + writer.write("foo"); + writer.close(); + + new ContentInputStream(channel.toReadable()).finalize(); + } + +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentOutputStreamTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentOutputStreamTestCase.java new file mode 100644 index 00000000000..00ea92ff246 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentOutputStreamTestCase.java @@ -0,0 +1,70 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import org.junit.Test; +import org.mockito.Mockito; + +import java.nio.ByteBuffer; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class FastContentOutputStreamTestCase { + + @Test + public void requireThatNullConstructorArgumentThrows() { + try { + new FastContentOutputStream((ContentChannel)null); + fail(); + } catch (NullPointerException e) { + assertEquals("out", e.getMessage()); + } + try { + new FastContentOutputStream((FastContentWriter)null); + fail(); + } catch (NullPointerException e) { + assertEquals("out", e.getMessage()); + } + } + + @Test + public void requireThatAllMethodsDelegateToWriter() throws Exception { + FastContentWriter writer = Mockito.mock(FastContentWriter.class); + FastContentOutputStream out = new FastContentOutputStream(writer); + + out.write(new byte[] { 6, 9 }); + out.flush(); + Mockito.verify(writer).write(Mockito.any(ByteBuffer.class)); + + out.close(); + Mockito.verify(writer).close(); + + out.cancel(true); + Mockito.verify(writer).cancel(true); + out.cancel(false); + Mockito.verify(writer).cancel(false); + + out.isCancelled(); + Mockito.verify(writer).isCancelled(); + + out.isDone(); + Mockito.verify(writer).isDone(); + + out.get(); + Mockito.verify(writer).get(); + + out.get(600, TimeUnit.SECONDS); + Mockito.verify(writer).get(600, TimeUnit.SECONDS); + + Runnable listener = Mockito.mock(Runnable.class); + Executor executor = Mockito.mock(Executor.class); + out.addListener(listener, executor); + Mockito.verify(writer).addListener(listener, executor); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentWriterTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentWriterTestCase.java new file mode 100644 index 00000000000..e3bedaf5c2a --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FastContentWriterTestCase.java @@ -0,0 +1,241 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import com.google.common.util.concurrent.MoreExecutors; +import org.junit.Test; +import org.mockito.Mockito; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertArrayEquals; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class FastContentWriterTestCase { + + @Test + public void requireThatContentCanBeWritten() throws ExecutionException, InterruptedException { + ReadableContentChannel content = new ReadableContentChannel(); + FastContentWriter out = new FastContentWriter(content); + + ByteBuffer foo = ByteBuffer.allocate(69); + out.write(foo); + ByteBuffer bar = ByteBuffer.allocate(69); + out.write(bar); + out.close(); + + assertFalse(out.isDone()); + assertSame(foo, content.read()); + assertFalse(out.isDone()); + assertSame(bar, content.read()); + assertFalse(out.isDone()); + assertNull(content.read()); + assertTrue(out.isDone()); + } + + @Test + public void requireThatStringsAreUtf8Encoded() { + ReadableContentChannel content = new ReadableContentChannel(); + FastContentWriter out = new FastContentWriter(content); + + String in = "\u6211\u80FD\u541E\u4E0B\u73BB\u7483\u800C\u4E0D\u4F24\u8EAB\u4F53\u3002"; + out.write(in); + out.close(); + + ByteBuffer buf = content.read(); + byte[] arr = new byte[buf.remaining()]; + buf.get(arr); + assertArrayEquals(in.getBytes(StandardCharsets.UTF_8), arr); + } + + @Test + public void requireThatCancelThrowsUnsupportedOperation() { + try { + new FastContentWriter(Mockito.mock(ContentChannel.class)).cancel(true); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + @Test + public void requireThatCancelIsAlwaysFalse() { + FastContentWriter writer = new FastContentWriter(Mockito.mock(ContentChannel.class)); + assertFalse(writer.isCancelled()); + try { + writer.cancel(true); + fail(); + } catch (UnsupportedOperationException e) { + + } + assertFalse(writer.isCancelled()); + } + + @Test + public void requireThatGetThrowsTimeoutUntilCloseCompletionHandlerIsCalled() throws Exception { + ReadableContentChannel buf = new ReadableContentChannel(); + FastContentWriter out = new FastContentWriter(buf); + + out.write(new byte[] { 6, 9 }); + assertFalse(out.isDone()); + try { + out.get(100, TimeUnit.MILLISECONDS); + fail(); + } catch (TimeoutException e) { + + } + + assertNotNull(buf.read()); + assertFalse(out.isDone()); + try { + out.get(100, TimeUnit.MILLISECONDS); + fail(); + } catch (TimeoutException e) { + + } + + out.close(); + assertFalse(out.isDone()); + try { + out.get(100, TimeUnit.MILLISECONDS); + fail(); + } catch (TimeoutException e) { + + } + + assertNull(buf.read()); + assertTrue(out.isDone()); + assertTrue(out.get(600, TimeUnit.SECONDS)); + assertTrue(out.get()); + } + + @Test + public void requireThatSyncWriteExceptionFailsFuture() throws InterruptedException { + IllegalStateException expected = new IllegalStateException(); + ContentChannel content = Mockito.mock(ContentChannel.class); + Mockito.doThrow(expected) + .when(content).write(Mockito.any(ByteBuffer.class), Mockito.any(CompletionHandler.class)); + FastContentWriter out = new FastContentWriter(content); + try { + out.write("foo"); + fail(); + } catch (Throwable t) { + assertSame(expected, t); + } + try { + out.get(); + fail(); + } catch (ExecutionException e) { + assertSame(expected, e.getCause()); + } + } + + @Test + public void requireThatSyncCloseExceptionFailsFuture() throws InterruptedException { + IllegalStateException expected = new IllegalStateException(); + ContentChannel content = Mockito.mock(ContentChannel.class); + Mockito.doThrow(expected) + .when(content).close(Mockito.any(CompletionHandler.class)); + FastContentWriter out = new FastContentWriter(content); + try { + out.close(); + fail(); + } catch (Throwable t) { + assertSame(expected, t); + } + try { + out.get(); + fail(); + } catch (ExecutionException e) { + assertSame(expected, e.getCause()); + } + } + + @Test + public void requireThatAsyncExceptionFailsFuture() throws InterruptedException { + IllegalStateException expected = new IllegalStateException(); + ReadableContentChannel content = new ReadableContentChannel(); + FastContentWriter out = new FastContentWriter(content); + out.write("foo"); + content.failed(expected); + try { + out.get(); + fail(); + } catch (ExecutionException e) { + assertSame(expected, e.getCause()); + } + } + + @Test + public void requireThatWriterCanBeListenedTo() throws InterruptedException { + ReadableContentChannel buf = new ReadableContentChannel(); + FastContentWriter out = new FastContentWriter(buf); + RunnableLatch listener = new RunnableLatch(); + out.addListener(listener, MoreExecutors.sameThreadExecutor()); + + out.write(new byte[] { 6, 9 }); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + assertNotNull(buf.read()); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + out.close(); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + assertNull(buf.read()); + assertTrue(listener.await(600, TimeUnit.SECONDS)); + } + + @Test + public void requireThatWriterIsThreadSafe() throws Exception { + final CountDownLatch latch = new CountDownLatch(2); + final ReadableContentChannel content = new ReadableContentChannel(); + Future<Integer> read = Executors.newSingleThreadExecutor().submit(new Callable<Integer>() { + + @Override + public Integer call() throws Exception { + latch.countDown(); + latch.await(600, TimeUnit.SECONDS); + + int bufCnt = 0; + while (content.read() != null) { + ++bufCnt; + } + return bufCnt; + } + }); + Future<Integer> write = Executors.newSingleThreadExecutor().submit(new Callable<Integer>() { + + @Override + public Integer call() throws Exception { + FastContentWriter out = new FastContentWriter(content); + ByteBuffer buf = ByteBuffer.wrap(new byte[69]); + int bufCnt = 4096 + new Random().nextInt(4096); + + latch.countDown(); + latch.await(600, TimeUnit.SECONDS); + for (int i = 0; i < bufCnt; ++i) { + out.write(buf.slice()); + } + out.close(); + return bufCnt; + } + }); + assertEquals(read.get(600, TimeUnit.SECONDS), + write.get(600, TimeUnit.SECONDS)); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureCompletionTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureCompletionTestCase.java new file mode 100644 index 00000000000..e886d663a72 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureCompletionTestCase.java @@ -0,0 +1,106 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import com.google.common.util.concurrent.MoreExecutors; +import org.junit.Test; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class FutureCompletionTestCase { + + @Test + public void requireThatCancelIsUnsupported() { + FutureCompletion future = new FutureCompletion(); + assertFalse(future.isCancelled()); + try { + future.cancel(true); + fail(); + } catch (UnsupportedOperationException e) { + + } + assertFalse(future.isCancelled()); + try { + future.cancel(false); + fail(); + } catch (UnsupportedOperationException e) { + + } + assertFalse(future.isCancelled()); + } + + @Test + public void requireThatCompletedReturnsTrue() throws Exception { + FutureCompletion future = new FutureCompletion(); + try { + future.get(0, TimeUnit.MILLISECONDS); + fail(); + } catch (TimeoutException e) { + + } + future.completed(); + assertTrue(future.get(0, TimeUnit.MILLISECONDS)); + assertTrue(future.get()); + } + + @Test + public void requireThatCompletionIsDoneWhenCompleted() { + FutureCompletion future = new FutureCompletion(); + assertFalse(future.isDone()); + future.completed(); + assertTrue(future.isDone()); + } + + @Test + public void requireThatCompletionIsDoneWhenFailed() { + FutureCompletion future = new FutureCompletion(); + assertFalse(future.isDone()); + future.failed(new Throwable()); + assertTrue(future.isDone()); + } + + @Test + public void requireThatFailedCauseIsRethrown() throws Exception { + FutureCompletion future = new FutureCompletion(); + Throwable t = new Throwable(); + future.failed(t); + try { + future.get(0, TimeUnit.SECONDS); + fail(); + } catch (ExecutionException e) { + assertSame(t, e.getCause()); + } + try { + future.get(); + fail(); + } catch (ExecutionException e) { + assertSame(t, e.getCause()); + } + } + + @Test + public void requireThatCompletionCanBeListenedTo() throws InterruptedException { + FutureCompletion completion = new FutureCompletion(); + RunnableLatch listener = new RunnableLatch(); + completion.addListener(listener, MoreExecutors.sameThreadExecutor()); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + completion.completed(); + assertTrue(listener.await(600, TimeUnit.SECONDS)); + + completion = new FutureCompletion(); + listener = new RunnableLatch(); + completion.addListener(listener, MoreExecutors.sameThreadExecutor()); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + completion.failed(new Throwable()); + assertTrue(listener.await(600, TimeUnit.SECONDS)); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureConjunctionTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureConjunctionTestCase.java new file mode 100644 index 00000000000..3916eb7ffd6 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureConjunctionTestCase.java @@ -0,0 +1,255 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import com.google.common.util.concurrent.AbstractFuture; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import org.junit.Test; + +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class FutureConjunctionTestCase { + + private final ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool()); + + @Test + public void requireThatAllFuturesAreWaitedFor() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + FutureConjunction future = new FutureConjunction(); + future.addOperand(executor.submit(new Callable<Boolean>() { + + @Override + public Boolean call() throws Exception { + return latch.await(600, TimeUnit.SECONDS); + } + })); + try { + future.get(100, TimeUnit.MILLISECONDS); + fail(); + } catch (TimeoutException e) { + + } + latch.countDown(); + assertTrue(future.get(600, TimeUnit.SECONDS)); + } + + @Test + public void requireThatGetReturnValueIsAConjunction() throws Exception { + assertTrue(tryGet(true)); + assertTrue(tryGet(true, true)); + assertTrue(tryGet(true, true, true)); + + assertFalse(tryGet(false)); + assertFalse(tryGet(false, true)); + assertFalse(tryGet(true, false)); + assertFalse(tryGet(false, true, true)); + assertFalse(tryGet(true, false, true)); + assertFalse(tryGet(true, true, false)); + assertFalse(tryGet(false, false, true)); + assertFalse(tryGet(false, true, false)); + assertFalse(tryGet(true, false, false)); + } + + @Test + public void requireThatIsDoneReturnValueIsAConjunction() { + assertTrue(tryIsDone(true)); + assertTrue(tryIsDone(true, true)); + assertTrue(tryIsDone(true, true, true)); + + assertFalse(tryIsDone(false)); + assertFalse(tryIsDone(false, true)); + assertFalse(tryIsDone(true, false)); + assertFalse(tryIsDone(false, true, true)); + assertFalse(tryIsDone(true, false, true)); + assertFalse(tryIsDone(true, true, false)); + assertFalse(tryIsDone(false, false, true)); + assertFalse(tryIsDone(false, true, false)); + assertFalse(tryIsDone(true, false, false)); + } + + @Test + public void requireThatCancelReturnValueIsAConjuction() { + assertTrue(tryCancel(true)); + assertTrue(tryCancel(true, true)); + assertTrue(tryCancel(true, true, true)); + + assertFalse(tryCancel(false)); + assertFalse(tryCancel(false, true)); + assertFalse(tryCancel(true, false)); + assertFalse(tryCancel(false, true, true)); + assertFalse(tryCancel(true, false, true)); + assertFalse(tryCancel(true, true, false)); + assertFalse(tryCancel(false, false, true)); + assertFalse(tryCancel(false, true, false)); + assertFalse(tryCancel(true, false, false)); + } + + @Test + public void requireThatIsCancelledReturnValueIsAConjuction() { + assertTrue(tryIsCancelled(true)); + assertTrue(tryIsCancelled(true, true)); + assertTrue(tryIsCancelled(true, true, true)); + + assertFalse(tryIsCancelled(false)); + assertFalse(tryIsCancelled(false, true)); + assertFalse(tryIsCancelled(true, false)); + assertFalse(tryIsCancelled(false, true, true)); + assertFalse(tryIsCancelled(true, false, true)); + assertFalse(tryIsCancelled(true, true, false)); + assertFalse(tryIsCancelled(false, false, true)); + assertFalse(tryIsCancelled(false, true, false)); + assertFalse(tryIsCancelled(true, false, false)); + } + + @Test + public void requireThatConjunctionCanBeListenedTo() throws InterruptedException { + FutureConjunction conjunction = new FutureConjunction(); + RunnableLatch listener = new RunnableLatch(); + conjunction.addListener(listener, MoreExecutors.sameThreadExecutor()); + assertTrue(listener.await(600, TimeUnit.SECONDS)); + + conjunction = new FutureConjunction(); + FutureBoolean foo = new FutureBoolean(); + conjunction.addOperand(foo); + FutureBoolean bar = new FutureBoolean(); + conjunction.addOperand(bar); + listener = new RunnableLatch(); + conjunction.addListener(listener, MoreExecutors.sameThreadExecutor()); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + foo.set(true); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + bar.set(true); + assertTrue(listener.await(600, TimeUnit.SECONDS)); + + conjunction = new FutureConjunction(); + foo = new FutureBoolean(); + conjunction.addOperand(foo); + bar = new FutureBoolean(); + conjunction.addOperand(bar); + listener = new RunnableLatch(); + conjunction.addListener(listener, MoreExecutors.sameThreadExecutor()); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + bar.set(true); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + foo.set(true); + assertTrue(listener.await(600, TimeUnit.SECONDS)); + } + + private static boolean tryGet(boolean... operands) throws Exception { + FutureConjunction foo = new FutureConjunction(); + FutureConjunction bar = new FutureConjunction(); + for (boolean op : operands) { + foo.addOperand(MyFuture.newInstance(op)); + bar.addOperand(MyFuture.newInstance(op)); + } + boolean fooResult = foo.get(); + boolean barResult = foo.get(0, TimeUnit.SECONDS); + assertEquals(fooResult, barResult); + return fooResult; + } + + private static boolean tryIsDone(boolean... operands) { + FutureConjunction foo = new FutureConjunction(); + for (boolean op : operands) { + foo.addOperand(MyFuture.newIsDone(op)); + } + return foo.isDone(); + } + + private static boolean tryCancel(boolean... operands) { + FutureConjunction foo = new FutureConjunction(); + FutureConjunction bar = new FutureConjunction(); + for (boolean op : operands) { + foo.addOperand(MyFuture.newCanCancel(op)); + bar.addOperand(MyFuture.newCanCancel(op)); + } + boolean fooResult = foo.cancel(true); + boolean barResult = foo.cancel(false); + assertEquals(fooResult, barResult); + return fooResult; + } + + private static boolean tryIsCancelled(boolean... operands) { + FutureConjunction foo = new FutureConjunction(); + for (boolean op : operands) { + foo.addOperand(MyFuture.newIsCancelled(op)); + } + return foo.isCancelled(); + } + + private static class FutureBoolean extends AbstractFuture<Boolean> { + + public boolean set(Boolean val) { + return super.set(val); + } + } + + private static class MyFuture extends AbstractFuture<Boolean> { + + final boolean value; + final boolean isDone; + final boolean canCancel; + final boolean isCancelled; + + MyFuture(boolean value, boolean isDone, boolean canCancel, boolean isCancelled) { + this.value = value; + this.isDone = isDone; + this.canCancel = canCancel; + this.isCancelled = isCancelled; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return canCancel; + } + + @Override + public boolean isCancelled() { + return isCancelled; + } + + @Override + public boolean isDone() { + return isDone; + } + + @Override + public Boolean get() { + return value; + } + + @Override + public Boolean get(long timeout, TimeUnit unit) { + return value; + } + + static ListenableFuture<Boolean> newInstance(boolean value) { + return new MyFuture(value, false, false, false); + } + + static ListenableFuture<Boolean> newIsDone(boolean isDone) { + return new MyFuture(false, isDone, false, false); + } + + static ListenableFuture<Boolean> newCanCancel(boolean canCancel) { + return new MyFuture(false, false, canCancel, false); + } + + static ListenableFuture<Boolean> newIsCancelled(boolean isCancelled) { + return new MyFuture(false, false, false, isCancelled); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureResponseTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureResponseTestCase.java new file mode 100644 index 00000000000..925c09f763d --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/FutureResponseTestCase.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.jdisc.handler; + +import com.google.common.util.concurrent.MoreExecutors; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.test.NonWorkingContentChannel; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class FutureResponseTestCase { + + @Test + public void requireThatCancelIsUnsupported() { + FutureResponse future = new FutureResponse(); + assertFalse(future.isCancelled()); + try { + future.cancel(true); + fail(); + } catch (UnsupportedOperationException e) { + + } + assertFalse(future.isCancelled()); + try { + future.cancel(false); + fail(); + } catch (UnsupportedOperationException e) { + + } + assertFalse(future.isCancelled()); + } + + @Test + public void requireThatCompletionIsDoneWhenHandlerIsCalled() { + FutureResponse future = new FutureResponse(); + assertFalse(future.isDone()); + future.handleResponse(new Response(69)); + assertTrue(future.isDone()); + } + + @Test + public void requireThatResponseBecomesAvailable() throws Exception { + FutureResponse future = new FutureResponse(); + try { + future.get(0, TimeUnit.MILLISECONDS); + fail(); + } catch (TimeoutException e) { + + } + Response response = new Response(Response.Status.OK); + future.handleResponse(response); + assertSame(response, future.get(0, TimeUnit.MILLISECONDS)); + } + + @Test + public void requireThatResponseContentIsReturnedToCaller() throws Exception { + ContentChannel content = new NonWorkingContentChannel(); + FutureResponse future = new FutureResponse(content); + Response response = new Response(Response.Status.OK); + assertSame(content, future.handleResponse(response)); + } + + @Test + public void requireThatResponseCanBeListenedTo() throws InterruptedException { + FutureResponse response = new FutureResponse(); + RunnableLatch listener = new RunnableLatch(); + response.addListener(listener, MoreExecutors.sameThreadExecutor()); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + response.handleResponse(new Response(Response.Status.OK)); + assertTrue(listener.await(600, TimeUnit.SECONDS)); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/NullContentTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/NullContentTestCase.java new file mode 100644 index 00000000000..40f6cd88216 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/NullContentTestCase.java @@ -0,0 +1,48 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import org.junit.Test; +import org.mockito.Mockito; + +import java.nio.ByteBuffer; + +import static org.junit.Assert.fail; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class NullContentTestCase { + + @Test + public void requireThatWriteThrowsException() { + CompletionHandler completion = Mockito.mock(CompletionHandler.class); + try { + NullContent.INSTANCE.write(ByteBuffer.allocate(69), completion); + fail(); + } catch (UnsupportedOperationException e) { + + } + Mockito.verifyZeroInteractions(completion); + } + + @Test + public void requireThatWriteEmptyDoesNotThrowException() { + CompletionHandler completion = Mockito.mock(CompletionHandler.class); + NullContent.INSTANCE.write(ByteBuffer.allocate(0), completion); + Mockito.verify(completion).completed(); + Mockito.verifyNoMoreInteractions(completion); + } + + @Test + public void requireThatCloseCallsCompletion() { + CompletionHandler completion = Mockito.mock(CompletionHandler.class); + NullContent.INSTANCE.close(completion); + Mockito.verify(completion).completed(); + Mockito.verifyNoMoreInteractions(completion); + } + + @Test + public void requireThatCloseWithoutCompletionDoesNotThrow() { + NullContent.INSTANCE.close(null); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ReadableContentChannelTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ReadableContentChannelTestCase.java new file mode 100644 index 00000000000..378da5449c2 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ReadableContentChannelTestCase.java @@ -0,0 +1,320 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.concurrent.*; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ReadableContentChannelTestCase { + + @Test + public void requireThatWriteNullThrowsException() { + ReadableContentChannel content = new ReadableContentChannel(); + try { + content.write(null, new MyCompletion()); + fail(); + } catch (NullPointerException e) { + + } + } + + @Test + public void requireThatWriteAfterCloseThrowsException() { + ReadableContentChannel content = new ReadableContentChannel(); + content.close(null); + try { + content.write(ByteBuffer.allocate(69), new MyCompletion()); + fail(); + } catch (IllegalStateException e) { + + } + } + + @Test + public void requireThatWriteAfterFailedThrowsException() { + ReadableContentChannel content = new ReadableContentChannel(); + content.failed(new RuntimeException()); + try { + content.write(ByteBuffer.allocate(69), new MyCompletion()); + fail(); + } catch (IllegalStateException e) { + + } + } + + @Test + public void requireThatCloseAfterCloseThrowsException() { + ReadableContentChannel content = new ReadableContentChannel(); + content.close(null); + try { + content.close(null); + fail(); + } catch (IllegalStateException e) { + + } + } + + @Test + public void requireThatCloseAfterFailedThrowsException() { + ReadableContentChannel content = new ReadableContentChannel(); + content.failed(new RuntimeException()); + try { + content.close(null); + fail(); + } catch (IllegalStateException e) { + + } + } + + @Test + public void requireThatFailedAfterFailedThrowsException() { + ReadableContentChannel content = new ReadableContentChannel(); + content.failed(new RuntimeException()); + try { + content.failed(new RuntimeException()); + fail(); + } catch (IllegalStateException e) { + + } + } + + @Test + public void requireThatIteratorDoesNotSupportRemove() { + try { + new ReadableContentChannel().iterator().remove(); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + @Test + public void requireThatWrittenBufferCanBeRead() { + ReadableContentChannel content = new ReadableContentChannel(); + ByteBuffer buf = ByteBuffer.allocate(69); + content.write(buf, null); + assertSame(buf, content.read()); + } + + @Test + public void requireThatWrittenBuffersAreReadInOrder() { + ReadableContentChannel content = new ReadableContentChannel(); + ByteBuffer foo = ByteBuffer.allocate(69); + content.write(foo, null); + ByteBuffer bar = ByteBuffer.allocate(69); + content.write(bar, null); + content.close(null); + assertSame(foo, content.read()); + assertSame(bar, content.read()); + } + + @Test + public void requireThatReadAfterCloseIsNull() { + ReadableContentChannel content = new ReadableContentChannel(); + content.close(null); + assertNull(content.read()); + assertNull(content.read()); + } + + @Test + public void requireThatWrittenBufferCanBeReadByIterator() { + ReadableContentChannel content = new ReadableContentChannel(); + ByteBuffer foo = ByteBuffer.allocate(69); + content.write(foo, null); + ByteBuffer bar = ByteBuffer.allocate(69); + content.write(bar, null); + content.close(null); + + Iterator<ByteBuffer> it = content.iterator(); + assertTrue(it.hasNext()); + assertSame(foo, it.next()); + assertTrue(it.hasNext()); + assertSame(bar, it.next()); + assertFalse(it.hasNext()); + try { + it.next(); + fail(); + } catch (NoSuchElementException e) { + + } + } + + @Test + public void requireThatReadAfterFailedIsNull() { + ReadableContentChannel content = new ReadableContentChannel(); + content.failed(new RuntimeException()); + assertNull(content.read()); + assertNull(content.read()); + } + + @Test + public void requireThatReadCallsCompletion() { + ReadableContentChannel content = new ReadableContentChannel(); + ByteBuffer buf = ByteBuffer.allocate(69); + MyCompletion completion = new MyCompletion(); + content.write(buf, completion); + assertFalse(completion.completed); + assertSame(buf, content.read()); + assertTrue(completion.completed); + + completion = new MyCompletion(); + content.close(completion); + assertFalse(completion.completed); + assertNull(content.read()); + assertTrue(completion.completed); + } + + @Test + public void requireThatReadWaitsForWrite() throws Exception { + ExecutorService executor = Executors.newSingleThreadExecutor(); + ReadableContentChannel content = new ReadableContentChannel(); + Future<ByteBuffer> readBuf = executor.submit(new ReadTask(content)); + try { + readBuf.get(100, TimeUnit.MILLISECONDS); + fail(); + } catch (TimeoutException e) { + + } + ByteBuffer buf = ByteBuffer.allocate(69); + content.write(buf, null); + assertSame(buf, readBuf.get(600, TimeUnit.SECONDS)); + } + + @Test + public void requireThatCloseNotifiesRead() throws Exception { + ExecutorService executor = Executors.newSingleThreadExecutor(); + ReadableContentChannel content = new ReadableContentChannel(); + Future<ByteBuffer> buf = executor.submit(new ReadTask(content)); + try { + buf.get(100, TimeUnit.MILLISECONDS); + fail(); + } catch (TimeoutException e) { + + } + content.close(null); + assertNull(buf.get(600, TimeUnit.SECONDS)); + } + + @Test + public void requireThatFailedNotifiesRead() throws Exception { + ExecutorService executor = Executors.newSingleThreadExecutor(); + ReadableContentChannel content = new ReadableContentChannel(); + Future<ByteBuffer> buf = executor.submit(new ReadTask(content)); + try { + buf.get(100, TimeUnit.MILLISECONDS); + fail(); + } catch (TimeoutException e) { + + } + content.failed(new RuntimeException()); + assertNull(buf.get(600, TimeUnit.SECONDS)); + } + + @Test + public void requireThatFailedCallsPendingCompletions() { + MyCompletion foo = new MyCompletion(); + MyCompletion bar = new MyCompletion(); + ReadableContentChannel content = new ReadableContentChannel(); + content.write(ByteBuffer.allocate(69), foo); + content.write(ByteBuffer.allocate(69), bar); + RuntimeException e = new RuntimeException(); + content.failed(e); + assertSame(e, foo.failed); + assertSame(e, bar.failed); + } + + @Test + public void requireThatAvailableIsNotBlocking() { + ReadableContentChannel content = new ReadableContentChannel(); + assertEquals(0, content.available()); + ByteBuffer buf = ByteBuffer.wrap(new byte[] { 6, 9 }); + content.write(buf, null); + assertTrue(content.available() > 0); + assertSame(buf, content.read()); + assertEquals(0, content.available()); + content.close(null); + assertNull(content.read()); + assertEquals(0, content.available()); + } + + @Test + public void requireThatContentIsThreadSafe() { + ExecutorService executor = Executors.newFixedThreadPool(100); + for (int run = 0; run < 69; ++run) { + List<ByteBuffer> bufs = new LinkedList<>(); + for (int buf = 0; buf < 100; ++buf) { + bufs.add(ByteBuffer.allocate(buf)); + } + ReadableContentChannel content = new ReadableContentChannel(); + for (ByteBuffer buf : bufs) { + executor.execute(new WriteTask(content, buf)); + } + for (int buf = 0; buf < 100; ++buf) { + assertTrue(bufs.remove(content.read())); + } + content.close(null); + assertNull(content.read()); + } + } + + private static class MyCompletion implements CompletionHandler { + + boolean completed = false; + Throwable failed = null; + + @Override + public void completed() { + completed = true; + } + + @Override + public void failed(Throwable t) { + failed = t; + } + } + + private static class ReadTask implements Callable<ByteBuffer> { + + final ReadableContentChannel content; + + ReadTask(ReadableContentChannel content) { + this.content = content; + } + + @Override + public ByteBuffer call() throws Exception { + return content.read(); + } + } + + private static class WriteTask implements Runnable { + + final ContentChannel content; + final ByteBuffer buf; + + WriteTask(ContentChannel content, ByteBuffer buf) { + this.content = content; + this.buf = buf; + } + + @Override + public void run() { + content.write(buf, null); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDeniedTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDeniedTestCase.java new file mode 100644 index 00000000000..3cfe794dfed --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDeniedTestCase.java @@ -0,0 +1,70 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import com.yahoo.jdisc.NoopSharedResource; +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertSame; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class RequestDeniedTestCase { + + @Test + public void requireThatAccessorsWork() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + driver.activateContainer(driver.newContainerBuilder()); + Request request = new Request(driver, URI.create("http://host/path")); + RequestDeniedException e = new RequestDeniedException(request); + assertSame(request, e.request()); + request.release(); + driver.close(); + } + + @Test + public void requireThatRequestDeniedIsThrown() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + RequestHandler requestHandler = new MyRequestHandler(); + builder.serverBindings().bind("http://host/path", requestHandler); + driver.activateContainer(builder); + Request request = new Request(driver, URI.create("http://host/path")); + try { + request.connect(new MyResponseHandler()); + fail(); + } catch (RequestDeniedException e) { + assertSame(request, e.request()); + } + request.release(); + driver.close(); + } + + private static class MyRequestHandler extends NoopSharedResource implements RequestHandler { + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + throw new RequestDeniedException(request); + } + + @Override + public void handleTimeout(Request request, ResponseHandler handler) { + + } + } + + private static class MyResponseHandler implements ResponseHandler { + + @Override + public ContentChannel handleResponse(Response response) { + return null; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDispatchTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDispatchTestCase.java new file mode 100644 index 00000000000..f13473a1660 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RequestDispatchTestCase.java @@ -0,0 +1,253 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import com.google.common.util.concurrent.MoreExecutors; +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class RequestDispatchTestCase { + + @Test + public void requireThatRequestCanBeDispatched() throws Exception { + final TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + final List<ByteBuffer> writtenContent = Arrays.asList(ByteBuffer.allocate(6), ByteBuffer.allocate(9)); + ReadableContentChannel receivedContent = new ReadableContentChannel(); + ContainerBuilder builder = driver.newContainerBuilder(); + Response response = new Response(Response.Status.OK); + builder.serverBindings().bind("http://localhost/", new MyRequestHandler(receivedContent, response)); + driver.activateContainer(builder); + RequestDispatch dispatch = new RequestDispatch() { + + @Override + protected Request newRequest() { + return new Request(driver, URI.create("http://localhost/")); + } + + @Override + protected Iterable<ByteBuffer> requestContent() { + return writtenContent; + } + }; + dispatch.dispatch(); + assertFalse(dispatch.isDone()); + assertSame(writtenContent.get(0), receivedContent.read()); + assertFalse(dispatch.isDone()); + assertSame(writtenContent.get(1), receivedContent.read()); + assertFalse(dispatch.isDone()); + assertNull(receivedContent.read()); + assertTrue(dispatch.isDone()); + assertSame(response, dispatch.get(600, TimeUnit.SECONDS)); + assertTrue(driver.close()); + } + + @Test + public void requireThatStreamCanBeConnected() throws IOException { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + ReadableContentChannel content = new ReadableContentChannel(); + MyRequestHandler requestHandler = new MyRequestHandler(content, new Response(Response.Status.OK)); + builder.serverBindings().bind("http://localhost/", requestHandler); + driver.activateContainer(builder); + + OutputStream out = new FastContentOutputStream(driver.newRequestDispatch("http://localhost/", new FutureResponse()).connect()); + out.write(6); + out.write(9); + out.close(); + + InputStream in = content.toStream(); + assertEquals(6, in.read()); + assertEquals(9, in.read()); + assertEquals(-1, in.read()); + + assertTrue(driver.close()); + } + + @Test + public void requireThatCancelIsUnsupported() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + RequestDispatch dispatch = driver.newRequestDispatch("http://localhost/", new FutureResponse()); + assertFalse(dispatch.isCancelled()); + try { + dispatch.cancel(true); + fail(); + } catch (UnsupportedOperationException e) { + + } + assertFalse(dispatch.isCancelled()); + try { + dispatch.cancel(false); + fail(); + } catch (UnsupportedOperationException e) { + + } + assertFalse(dispatch.isCancelled()); + assertTrue(driver.close()); + } + + @Test + public void requireThatDispatchHandlesConnectException() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind("http://localhost/", new AbstractRequestHandler() { + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + throw new RuntimeException(); + } + }); + driver.activateContainer(builder); + try { + driver.newRequestDispatch("http://localhost/", new FutureResponse()).dispatch(); + fail(); + } catch (RuntimeException e) { + + } + assertTrue(driver.close()); + } + + @Test + public void requireThatDispatchHandlesWriteException() { + final TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + Response response = new Response(Response.Status.OK); + builder.serverBindings().bind("http://localhost/", new MyRequestHandler(new ContentChannel() { + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + throw new RuntimeException(); + } + + @Override + public void close(CompletionHandler handler) { + handler.completed(); + } + }, response)); + driver.activateContainer(builder); + try { + new RequestDispatch() { + + @Override + protected Request newRequest() { + return new Request(driver, URI.create("http://localhost/")); + } + + @Override + protected Iterable<ByteBuffer> requestContent() { + return Arrays.asList(ByteBuffer.allocate(69)); + } + }.dispatch(); + fail(); + } catch (RuntimeException e) { + + } + assertTrue(driver.close()); + } + + @Test + public void requireThatDispatchHandlesCloseException() { + final TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + Response response = new Response(Response.Status.OK); + builder.serverBindings().bind("http://localhost/", new MyRequestHandler(new ContentChannel() { + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + handler.completed(); + } + + @Override + public void close(CompletionHandler handler) { + throw new RuntimeException(); + } + }, response)); + driver.activateContainer(builder); + try { + new RequestDispatch() { + + @Override + protected Request newRequest() { + return new Request(driver, URI.create("http://localhost/")); + } + + @Override + protected Iterable<ByteBuffer> requestContent() { + return Arrays.asList(ByteBuffer.allocate(69)); + } + }.dispatch(); + fail(); + } catch (RuntimeException e) { + + } + assertTrue(driver.close()); + } + + @Test + public void requireThatDispatchCanBeListenedTo() throws InterruptedException { + final TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + ReadableContentChannel requestContent = new ReadableContentChannel(); + MyRequestHandler requestHandler = new MyRequestHandler(requestContent, null); + builder.serverBindings().bind("http://localhost/", requestHandler); + driver.activateContainer(builder); + RunnableLatch listener = new RunnableLatch(); + new RequestDispatch() { + + @Override + protected Request newRequest() { + return new Request(driver, URI.create("http://localhost/")); + } + }.dispatch().addListener(listener, MoreExecutors.sameThreadExecutor()); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + ContentChannel responseContent = ResponseDispatch.newInstance(Response.Status.OK) + .connect(requestHandler.responseHandler); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + assertNull(requestContent.read()); + assertTrue(listener.await(600, TimeUnit.SECONDS)); + responseContent.close(null); + assertTrue(driver.close()); + } + + private static class MyRequestHandler extends AbstractRequestHandler { + + final ContentChannel content; + final Response response; + ResponseHandler responseHandler; + + MyRequestHandler(ContentChannel content, Response response) { + this.content = content; + this.response = response; + } + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + if (response != null) { + ResponseDispatch.newInstance(response).dispatch(handler); + } else { + responseHandler = handler; + } + return content; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ResponseDispatchTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ResponseDispatchTestCase.java new file mode 100644 index 00000000000..92fc0f90c07 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ResponseDispatchTestCase.java @@ -0,0 +1,206 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import com.google.common.util.concurrent.MoreExecutors; +import com.yahoo.jdisc.Response; +import org.junit.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class ResponseDispatchTestCase { + + @Test + public void requireThatFactoryMethodsWork() throws Exception { + { + FutureResponse handler = new FutureResponse(); + ResponseDispatch.newInstance(69).dispatch(handler); + Response response = handler.get(600, TimeUnit.SECONDS); + assertNotNull(response); + assertEquals(69, response.getStatus()); + } + { + FutureResponse handler = new FutureResponse(); + Response sentResponse = new Response(69); + ResponseDispatch.newInstance(sentResponse).dispatch(handler); + Response receivedResponse = handler.get(600, TimeUnit.SECONDS); + assertSame(sentResponse, receivedResponse); + } + { + ReadableContentChannel content = new ReadableContentChannel(); + FutureResponse handler = new FutureResponse(content); + ByteBuffer buf = ByteBuffer.allocate(69); + ResponseDispatch.newInstance(69, Arrays.asList(buf)).dispatch(handler); + Response response = handler.get(600, TimeUnit.SECONDS); + assertNotNull(response); + assertEquals(69, response.getStatus()); + assertSame(buf, content.read()); + assertNull(content.read()); + } + { + ReadableContentChannel content = new ReadableContentChannel(); + FutureResponse handler = new FutureResponse(content); + ByteBuffer buf = ByteBuffer.allocate(69); + ResponseDispatch.newInstance(69, Arrays.asList(buf)).dispatch(handler); + Response response = handler.get(600, TimeUnit.SECONDS); + assertNotNull(response); + assertEquals(69, response.getStatus()); + assertSame(buf, content.read()); + assertNull(content.read()); + } + { + ReadableContentChannel content = new ReadableContentChannel(); + FutureResponse handler = new FutureResponse(content); + ByteBuffer buf = ByteBuffer.allocate(69); + Response sentResponse = new Response(69); + ResponseDispatch.newInstance(sentResponse, Arrays.asList(buf)).dispatch(handler); + Response receivedResponse = handler.get(600, TimeUnit.SECONDS); + assertSame(sentResponse, receivedResponse); + assertSame(buf, content.read()); + assertNull(content.read()); + } + } + + @Test + public void requireThatResponseCanBeDispatched() throws Exception { + final Response response = new Response(Response.Status.OK); + final List<ByteBuffer> writtenContent = Arrays.asList(ByteBuffer.allocate(6), ByteBuffer.allocate(9)); + ResponseDispatch dispatch = new ResponseDispatch() { + + @Override + protected Response newResponse() { + return response; + } + + @Override + protected Iterable<ByteBuffer> responseContent() { + return writtenContent; + } + }; + ReadableContentChannel receivedContent = new ReadableContentChannel(); + MyResponseHandler responseHandler = new MyResponseHandler(receivedContent); + dispatch.dispatch(responseHandler); + assertFalse(dispatch.isDone()); + assertSame(response, responseHandler.response); + assertSame(writtenContent.get(0), receivedContent.read()); + assertFalse(dispatch.isDone()); + assertSame(writtenContent.get(1), receivedContent.read()); + assertFalse(dispatch.isDone()); + assertNull(receivedContent.read()); + assertTrue(dispatch.isDone()); + assertTrue(dispatch.get(600, TimeUnit.SECONDS)); + assertTrue(dispatch.get()); + } + + @Test + public void requireThatStreamCanBeConnected() throws IOException { + ReadableContentChannel responseContent = new ReadableContentChannel(); + OutputStream out = new FastContentOutputStream(new ResponseDispatch() { + + @Override + protected Response newResponse() { + return new Response(Response.Status.OK); + } + }.connect(new MyResponseHandler(responseContent))); + out.write(6); + out.write(9); + out.close(); + + InputStream in = responseContent.toStream(); + assertEquals(6, in.read()); + assertEquals(9, in.read()); + assertEquals(-1, in.read()); + } + + @Test + public void requireThatCancelIsUnsupported() { + ResponseDispatch dispatch = ResponseDispatch.newInstance(69); + assertFalse(dispatch.isCancelled()); + try { + dispatch.cancel(true); + fail(); + } catch (UnsupportedOperationException e) { + + } + assertFalse(dispatch.isCancelled()); + try { + dispatch.cancel(false); + fail(); + } catch (UnsupportedOperationException e) { + + } + assertFalse(dispatch.isCancelled()); + } + + @Test + public void requireThatDispatchClosesContentIfWriteThrowsException() { + final AtomicBoolean closed = new AtomicBoolean(false); + try { + ResponseDispatch.newInstance(6, ByteBuffer.allocate(9)).dispatch( + new MyResponseHandler(new ContentChannel() { + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + throw new UnsupportedOperationException(); + } + + @Override + public void close(CompletionHandler handler) { + closed.set(true); + } + })); + fail(); + } catch (UnsupportedOperationException e) { + + } + assertTrue(closed.get()); + } + + @Test + public void requireThatDispatchCanBeListenedTo() throws InterruptedException { + RunnableLatch listener = new RunnableLatch(); + ReadableContentChannel responseContent = new ReadableContentChannel(); + ResponseDispatch.newInstance(6, ByteBuffer.allocate(9)) + .dispatch(new MyResponseHandler(responseContent)) + .addListener(listener, MoreExecutors.sameThreadExecutor()); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + assertNotNull(responseContent.read()); + assertFalse(listener.await(100, TimeUnit.MILLISECONDS)); + assertNull(responseContent.read()); + assertTrue(listener.await(600, TimeUnit.SECONDS)); + } + + private static class MyResponseHandler implements ResponseHandler { + + final ContentChannel content; + Response response; + + MyResponseHandler(ContentChannel content) { + this.content = content; + } + + @Override + public ContentChannel handleResponse(Response response) { + this.response = response; + return content; + } + } + +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RunnableLatch.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RunnableLatch.java new file mode 100644 index 00000000000..e81d7eb16ef --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/RunnableLatch.java @@ -0,0 +1,22 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +class RunnableLatch implements Runnable { + + private final CountDownLatch latch = new CountDownLatch(1); + + @Override + public void run() { + latch.countDown(); + } + + public boolean await(long timeout, TimeUnit unit) throws InterruptedException { + return latch.await(timeout, unit); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ThreadedRequestHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ThreadedRequestHandlerTestCase.java new file mode 100644 index 00000000000..545be4e03ce --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/ThreadedRequestHandlerTestCase.java @@ -0,0 +1,228 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.handler; + +import com.google.common.util.concurrent.ListenableFuture; +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.service.CurrentContainer; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ThreadedRequestHandlerTestCase { + + @Test + public void requireThatNullExecutorThrowsException() { + try { + new ThreadedRequestHandler(null) { + + @Override + public void handleRequest(Request request, BufferedContentChannel content, ResponseHandler handler) { + + } + }; + fail(); + } catch (NullPointerException e) { + + } + } + + @Test + public void requireThatAccessorWork() { + MyRequestHandler requestHandler = new MyRequestHandler(newExecutor()); + requestHandler.setTimeout(1000, TimeUnit.MILLISECONDS); + assertEquals(1000, requestHandler.getTimeout(TimeUnit.MILLISECONDS)); + assertEquals(1, requestHandler.getTimeout(TimeUnit.SECONDS)); + } + + @Test + public void requireThatHandlerSetsRequestTimeout() throws InterruptedException { + MyRequestHandler requestHandler = new MyRequestHandler(newExecutor()); + requestHandler.setTimeout(600, TimeUnit.SECONDS); + TestDriver driver = newTestDriver("http://localhost/", requestHandler); + + MyResponseHandler responseHandler = new MyResponseHandler(); + driver.dispatchRequest("http://localhost/", responseHandler); + + requestHandler.entryLatch.countDown(); + assertTrue(requestHandler.exitLatch.await(600, TimeUnit.SECONDS)); + assertNull(requestHandler.content.read()); + assertNotNull(requestHandler.request.getTimeout(TimeUnit.MILLISECONDS)); + + assertTrue(responseHandler.latch.await(600, TimeUnit.SECONDS)); + assertNull(responseHandler.content.read()); + assertTrue(driver.close()); + } + + @Test + public void requireThatRequestAndResponseReachHandlers() throws InterruptedException { + MyRequestHandler requestHandler = new MyRequestHandler(newExecutor()); + TestDriver driver = newTestDriver("http://localhost/", requestHandler); + + MyResponseHandler responseHandler = new MyResponseHandler(); + Request request = new Request(driver, URI.create("http://localhost/")); + ContentChannel requestContent = request.connect(responseHandler); + ByteBuffer buf = ByteBuffer.allocate(69); + requestContent.write(buf, null); + requestContent.close(null); + request.release(); + + requestHandler.entryLatch.countDown(); + assertTrue(requestHandler.exitLatch.await(600, TimeUnit.SECONDS)); + assertSame(request, requestHandler.request); + assertSame(buf, requestHandler.content.read()); + assertNull(requestHandler.content.read()); + + assertTrue(responseHandler.latch.await(600, TimeUnit.SECONDS)); + assertSame(requestHandler.response, responseHandler.response); + assertNull(responseHandler.content.read()); + assertTrue(driver.close()); + } + + @Test + public void requireThatNotImplementedHandlerDoesNotPreventShutdown() throws Exception { + TestDriver driver = newTestDriver("http://localhost/", new ThreadedRequestHandler(newExecutor()) { + + }); + assertEquals(Response.Status.NOT_IMPLEMENTED, + dispatchRequest(driver, "http://localhost/", ByteBuffer.wrap(new byte[] { 69 })) + .get(600, TimeUnit.SECONDS).getStatus()); + assertTrue(driver.close()); + } + + @Test + public void requireThatThreadedRequestHandlerRetainsTheRequestUntilHandlerIsRun() throws Exception { + final TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + final AtomicInteger baseRetainCount = new AtomicInteger(); + builder.serverBindings().bind("http://localhost/base", new AbstractRequestHandler() { + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + baseRetainCount.set(request.retainCount()); + handler.handleResponse(new Response(Response.Status.OK)).close(null); + return null; + } + }); + final CountDownLatch entryLatch = new CountDownLatch(1); + final CountDownLatch exitLatch = new CountDownLatch(1); + final AtomicInteger testRetainCount = new AtomicInteger(); + builder.serverBindings().bind("http://localhost/test", new ThreadedRequestHandler(newExecutor()) { + + @Override + public void handleRequest(Request request, ReadableContentChannel requestContent, + ResponseHandler responseHandler) { + try { + entryLatch.await(600, TimeUnit.SECONDS); + } catch (InterruptedException e) { + return; + } + testRetainCount.set(request.retainCount()); + responseHandler.handleResponse(new Response(Response.Status.OK)).close(null); + requestContent.read(); // drain content to call completion handlers + exitLatch.countDown(); + } + }); + driver.activateContainer(builder); + dispatchRequest(driver, "http://localhost/base"); + dispatchRequest(driver, "http://localhost/test"); + entryLatch.countDown(); + exitLatch.await(600, TimeUnit.SECONDS); + assertEquals(baseRetainCount.get(), testRetainCount.get()); + assertTrue(driver.close()); + } + + private static TestDriver newTestDriver(String uri, RequestHandler requestHandler) { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind(uri, requestHandler); + driver.activateContainer(builder); + return driver; + } + + private static ListenableFuture<Response> dispatchRequest(final CurrentContainer container, final String uri, + final ByteBuffer... content) { + return new RequestDispatch() { + + @Override + protected Request newRequest() { + return new Request(container, URI.create(uri)); + } + + @Override + protected Iterable<ByteBuffer> requestContent() { + return Arrays.asList(content); + } + }.dispatch(); + } + + private static Executor newExecutor() { + return Executors.newSingleThreadExecutor(); + } + + private static class MyRequestHandler extends ThreadedRequestHandler { + + final CountDownLatch entryLatch = new CountDownLatch(1); + final CountDownLatch exitLatch = new CountDownLatch(1); + final ReadableContentChannel content = new ReadableContentChannel(); + Response response = null; + Request request = null; + + MyRequestHandler(Executor executor) { + super(executor); + } + + @Override + public void handleRequest(Request request, BufferedContentChannel content, ResponseHandler handler) { + try { + if (!entryLatch.await(600, TimeUnit.SECONDS)) { + return; + } + this.request = request; + content.connectTo(this.content); + response = new Response(Response.Status.OK); + handler.handleResponse(response).close(null); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + exitLatch.countDown(); + } + } + } + + private static class MyResponseHandler implements ResponseHandler { + + final CountDownLatch latch = new CountDownLatch(1); + final ReadableContentChannel content = new ReadableContentChannel(); + Response response = null; + + @Override + public ContentChannel handleResponse(Response response) { + this.response = response; + latch.countDown(); + + BufferedContentChannel content = new BufferedContentChannel(); + content.connectTo(this.content); + return content; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/handler/UnsafeContentInputStreamTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/UnsafeContentInputStreamTestCase.java new file mode 100644 index 00000000000..9aac2c4ea7f --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/handler/UnsafeContentInputStreamTestCase.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.jdisc.handler; + +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.ByteBuffer; +import java.util.concurrent.Future; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class UnsafeContentInputStreamTestCase { + + @Test + public void requireThatBytesCanBeRead() throws IOException { + BufferedContentChannel channel = new BufferedContentChannel(); + FastContentWriter writer = new FastContentWriter(channel); + writer.write("Hello "); + writer.write("World!"); + writer.close(); + + BufferedReader reader = asBufferedReader(channel); + assertEquals("Hello World!", reader.readLine()); + assertNull(reader.readLine()); + } + + @SuppressWarnings("deprecation") + @Test + public void requireThatCompletionsAreCalledWithDeprecatedContentWriter() throws IOException { + BufferedContentChannel channel = new BufferedContentChannel(); + FastContentWriter writer = new FastContentWriter(channel); + writer.write("foo"); + writer.close(); + + InputStream stream = asInputStream(channel); + assertEquals('f', stream.read()); + assertEquals('o', stream.read()); + assertEquals('o', stream.read()); + assertEquals(-1, stream.read()); + assertTrue(writer.isDone()); + } + + @Test + public void requireThatCompletionsAreCalled() throws IOException { + BufferedContentChannel channel = new BufferedContentChannel(); + FastContentWriter writer = new FastContentWriter(channel); + writer.write("foo"); + writer.close(); + + InputStream stream = asInputStream(channel); + assertEquals('f', stream.read()); + assertEquals('o', stream.read()); + assertEquals('o', stream.read()); + assertEquals(-1, stream.read()); + assertTrue(writer.isDone()); + } + + @SuppressWarnings("deprecation") + @Test + public void requireThatCloseDrainsStreamWithDeprecatedContentWriter() { + BufferedContentChannel channel = new BufferedContentChannel(); + FastContentWriter writer = new FastContentWriter(channel); + writer.write("foo"); + writer.close(); + + asInputStream(channel).close(); + assertTrue(writer.isDone()); + } + + @Test + public void requireThatCloseDrainsStream() { + BufferedContentChannel channel = new BufferedContentChannel(); + FastContentWriter writer = new FastContentWriter(channel); + writer.write("foo"); + writer.close(); + + asInputStream(channel).close(); + assertTrue(writer.isDone()); + } + + @Test + public void requireThatAvailableIsNotBlocking() throws IOException { + BufferedContentChannel channel = new BufferedContentChannel(); + InputStream stream = asInputStream(channel); + assertEquals(0, stream.available()); + channel.write(ByteBuffer.wrap(new byte[] { 6, 9 }), null); + assertTrue(stream.available() > 0); + assertEquals(6, stream.read()); + assertTrue(stream.available() > 0); + assertEquals(9, stream.read()); + assertEquals(0, stream.available()); + channel.close(null); + assertEquals(-1, stream.read()); + assertEquals(0, stream.available()); + } + + @Test + public void requireThatReadLargeArrayIsNotBlocking() throws IOException { + BufferedContentChannel channel = new BufferedContentChannel(); + InputStream stream = asInputStream(channel); + assertEquals(0, stream.available()); + channel.write(ByteBuffer.wrap(new byte[] { 6, 9 }), null); + assertTrue(stream.available() > 0); + byte[] buf = new byte[69]; + assertEquals(2, stream.read(buf)); + assertEquals(6, buf[0]); + assertEquals(9, buf[1]); + assertEquals(0, stream.available()); + channel.close(null); + assertEquals(-1, stream.read(buf)); + assertEquals(0, stream.available()); + } + + @Test + public void requireThatAllByteValuesCanBeRead() throws IOException { + ReadableContentChannel content = new ReadableContentChannel(); + InputStream in = new UnsafeContentInputStream(content); + for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; ++i) { + content.write(ByteBuffer.wrap(new byte[] { (byte)i }), null); + assertEquals(i, (byte)in.read()); + } + } + + private static BufferedReader asBufferedReader(BufferedContentChannel channel) { + return new BufferedReader(new InputStreamReader(asInputStream(channel))); + } + + private static UnsafeContentInputStream asInputStream(BufferedContentChannel channel) { + return new UnsafeContentInputStream(channel.toReadable()); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractClientProviderTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractClientProviderTestCase.java new file mode 100644 index 00000000000..93e96c29e6f --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractClientProviderTestCase.java @@ -0,0 +1,34 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.service; + +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.ResponseHandler; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class AbstractClientProviderTestCase { + + @Test + public void requireThatAbstractClassIsAClientProvider() { + assertTrue(ClientProvider.class.isInstance(new MyClientProvider())); + } + + @Test + public void requireThatStartDoesNotThrowAnException() { + new MyClientProvider().start(); + } + + private static class MyClientProvider extends AbstractClientProvider { + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + return null; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractServerProviderTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractServerProviderTestCase.java new file mode 100644 index 00000000000..c6230e928b7 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/AbstractServerProviderTestCase.java @@ -0,0 +1,51 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.service; + +import com.google.inject.Inject; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class AbstractServerProviderTestCase { + + @Test + public void requireThatAbstractClassIsAServerProvider() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + assertTrue(ServerProvider.class.isInstance(new MyServerProvider(driver))); + assertTrue(driver.close()); + } + + @Test + public void requireThatAccessorsWork() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + MyServerProvider server = builder.getInstance(MyServerProvider.class); + assertNotNull(server.container()); + assertTrue(driver.close()); + } + + private static class MyServerProvider extends AbstractServerProvider { + + @Inject + public MyServerProvider(CurrentContainer container) { + super(container); + } + + @Override + public void start() { + + } + + @Override + public void close() { + + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/BindingSetNotFoundTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/BindingSetNotFoundTestCase.java new file mode 100644 index 00000000000..6d3ed636972 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/BindingSetNotFoundTestCase.java @@ -0,0 +1,58 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.service; + +import com.google.inject.AbstractModule; +import com.yahoo.jdisc.application.BindingSetSelector; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class BindingSetNotFoundTestCase { + + @Test + public void requireThatAccessorsWork() { + BindingSetNotFoundException e = new BindingSetNotFoundException("foo"); + assertEquals("foo", e.bindingSet()); + } + + @Test + public void requireThatBindingSetNotFoundIsThrown() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(new AbstractModule() { + + @Override + protected void configure() { + bind(BindingSetSelector.class).toInstance(new MySelector("foo")); + } + }); + driver.activateContainer(driver.newContainerBuilder()); + try { + driver.newReference(URI.create("http://host")); + fail(); + } catch (BindingSetNotFoundException e) { + assertEquals("foo", e.bindingSet()); + } + driver.close(); + } + + private static class MySelector implements BindingSetSelector { + + final String setName; + + MySelector(String setName) { + this.setName = setName; + } + + @Override + public String select(URI uri) { + return setName; + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/ConnectToHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/ConnectToHandlerTestCase.java new file mode 100644 index 00000000000..a516e42c11a --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/ConnectToHandlerTestCase.java @@ -0,0 +1,99 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.service; + +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.handler.AbstractRequestHandler; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; +import java.nio.ByteBuffer; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ConnectToHandlerTestCase { + + @Test + public void requireThatNullResponseHandlerThrowsException() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + driver.activateContainer(driver.newContainerBuilder()); + Request request = new Request(driver, URI.create("http://host/path")); + try { + request.connect(null); + fail(); + } catch (NullPointerException e) { + // expected + } + request.release(); + driver.close(); + } + + @Test + public void requireThatConnectToHandlerWorks() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = new MyRequestHandler(new MyContent()); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind("http://host/*", requestHandler); + driver.activateContainer(builder); + Request request = new Request(driver, URI.create("http://host/path")); + MyResponseHandler responseHandler = new MyResponseHandler(); + ContentChannel content = request.connect(responseHandler); + request.release(); + assertNotNull(content); + content.close(null); + assertNotNull(requestHandler.handler); + assertSame(request, requestHandler.request); + requestHandler.handler.handleResponse(new Response(Response.Status.OK)).close(null); + driver.close(); + } + + private class MyRequestHandler extends AbstractRequestHandler { + + final ContentChannel content; + Request request; + ResponseHandler handler; + + MyRequestHandler(ContentChannel content) { + this.content = content; + } + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + this.request = request; + this.handler = handler; + return content; + } + } + + private class MyResponseHandler implements ResponseHandler { + + @Override + public ContentChannel handleResponse(Response response) { + return null; + } + } + + private static class MyContent implements ContentChannel { + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + handler.completed(); + } + + @Override + public void close(CompletionHandler handler) { + handler.completed(); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/ContainerNotReadyTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/ContainerNotReadyTestCase.java new file mode 100644 index 00000000000..423e6e4cc91 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/ContainerNotReadyTestCase.java @@ -0,0 +1,28 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.service; + +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; + +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class ContainerNotReadyTestCase { + + @Test + public void requireThatExceptionIsThrown() throws BindingSetNotFoundException { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + try { + driver.newReference(URI.create("http://host")); + fail(); + } catch (ContainerNotReadyException e) { + + } + driver.close(); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/CurrentContainerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/CurrentContainerTestCase.java new file mode 100644 index 00000000000..8a5a6aeb913 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/CurrentContainerTestCase.java @@ -0,0 +1,27 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.service; + +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; + +import static org.junit.Assert.assertNotNull; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class CurrentContainerTestCase { + + @Test + public void requireThatNewRequestsCreateSnapshot() throws Exception { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + driver.activateContainer(driver.newContainerBuilder()); + Request request = new Request(driver, URI.create("http://host/path")); + assertNotNull(request.container()); + request.release(); + driver.close(); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/service/NoBindingSetSelectedTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/service/NoBindingSetSelectedTestCase.java new file mode 100644 index 00000000000..56ea80c2001 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/service/NoBindingSetSelectedTestCase.java @@ -0,0 +1,55 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.service; + +import com.google.inject.AbstractModule; +import com.yahoo.jdisc.application.BindingSetSelector; +import com.yahoo.jdisc.test.TestDriver; +import org.junit.Test; + +import java.net.URI; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class NoBindingSetSelectedTestCase { + + @Test + public void requireThatAccessorsWork() { + URI uri = URI.create("http://host/path"); + NoBindingSetSelectedException e = new NoBindingSetSelectedException(uri); + assertEquals(uri, e.uri()); + } + + @Test + public void requireThatNoBindingSetSelectedIsThrown() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(new AbstractModule() { + + @Override + protected void configure() { + bind(BindingSetSelector.class).toInstance(new MySelector()); + } + }); + driver.activateContainer(driver.newContainerBuilder()); + URI uri = URI.create("http://host"); + try { + driver.newReference(uri); + fail(); + } catch (NoBindingSetSelectedException e) { + assertEquals(uri, e.uri()); + } + driver.close(); + } + + private static class MySelector implements BindingSetSelector { + + @Override + public String select(URI uri) { + return null; + } + } + +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingClientTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingClientTestCase.java new file mode 100644 index 00000000000..ff70ee35049 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingClientTestCase.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.jdisc.test; + +import com.yahoo.jdisc.service.ClientProvider; +import org.junit.Test; + +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class NonWorkingClientTestCase { + + @Test + public void requireThatHandleRequestThrowsException() { + ClientProvider client = new NonWorkingClientProvider(); + try { + client.handleRequest(null, null); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + @Test + public void requireThatHandleTimeoutThrowsException() { + ClientProvider client = new NonWorkingClientProvider(); + try { + client.handleTimeout(null, null); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + @Test + public void requireThatStartDoesNotThrow() { + ClientProvider client = new NonWorkingClientProvider(); + client.start(); + } + + @Test + public void requireThatRetainDoesNotThrow() { + ClientProvider client = new NonWorkingClientProvider(); + client.release(); + } + + @Test + public void requireThatReleaseDoesNotThrow() { + ClientProvider client = new NonWorkingClientProvider(); + client.release(); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingCompletionHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingCompletionHandlerTestCase.java new file mode 100644 index 00000000000..9ee5f5dd265 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingCompletionHandlerTestCase.java @@ -0,0 +1,43 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.test; + +import com.yahoo.jdisc.handler.CompletionHandler; +import org.junit.Test; + +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class NonWorkingCompletionHandlerTestCase { + + @Test + public void requireThatCompletedThrowsException() { + CompletionHandler completion = new NonWorkingCompletionHandler(); + try { + completion.completed(); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + @Test + public void requireThatFailedThrowsException() { + CompletionHandler completion = new NonWorkingCompletionHandler(); + try { + completion.failed(null); + fail(); + } catch (UnsupportedOperationException e) { + + } + completion = new NonWorkingCompletionHandler(); + try { + completion.failed(new Throwable()); + fail(); + } catch (UnsupportedOperationException e) { + + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingContentChannelTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingContentChannelTestCase.java new file mode 100644 index 00000000000..73adca81bf2 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingContentChannelTestCase.java @@ -0,0 +1,80 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.test; + +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import org.junit.Test; + +import java.nio.ByteBuffer; + +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class NonWorkingContentChannelTestCase { + + @Test + public void requireThatWriteThrowsException() { + ContentChannel content = new NonWorkingContentChannel(); + try { + content.write(null, null); + fail(); + } catch (UnsupportedOperationException e) { + + } + content = new NonWorkingContentChannel(); + try { + content.write(ByteBuffer.allocate(69), null); + fail(); + } catch (UnsupportedOperationException e) { + + } + content = new NonWorkingContentChannel(); + try { + content.write(ByteBuffer.allocate(69), new MyCompletion()); + fail(); + } catch (UnsupportedOperationException e) { + + } + content = new NonWorkingContentChannel(); + try { + content.write(null, new MyCompletion()); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + @Test + public void requireThatCloseThrowsException() { + ContentChannel content = new NonWorkingContentChannel(); + try { + content.close(null); + fail(); + } catch (UnsupportedOperationException e) { + + } + content = new NonWorkingContentChannel(); + try { + content.close(new MyCompletion()); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + private static class MyCompletion implements CompletionHandler { + + @Override + public void completed() { + + } + + @Override + public void failed(Throwable t) { + + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingOsgiFrameworkTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingOsgiFrameworkTestCase.java new file mode 100644 index 00000000000..ad1e34aba66 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingOsgiFrameworkTestCase.java @@ -0,0 +1,72 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.test; + +import com.yahoo.jdisc.application.OsgiFramework; +import org.junit.Test; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleException; + +import java.util.Collections; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class NonWorkingOsgiFrameworkTestCase { + + @Test + public void requireThatFrameworkCanStartAndStop() throws BundleException { + OsgiFramework osgi = new NonWorkingOsgiFramework(); + osgi.start(); + osgi.stop(); + } + + @Test + public void requireThatFrameworkHasNoBundles() throws BundleException { + OsgiFramework osgi = new NonWorkingOsgiFramework(); + assertTrue(osgi.bundles().isEmpty()); + } + + @Test + public void requireThatFrameworkHasNoBundleContext() { + OsgiFramework osgi = new NonWorkingOsgiFramework(); + assertNull(osgi.bundleContext()); + } + + @Test + public void requireThatFrameworkThrowsOnInstallBundle() throws BundleException { + OsgiFramework osgi = new NonWorkingOsgiFramework(); + try { + osgi.installBundle("file:bundle.jar"); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + @Test + public void requireThatFrameworkThrowsOnStartBundles() throws BundleException { + OsgiFramework osgi = new NonWorkingOsgiFramework(); + try { + osgi.startBundles(Collections.<Bundle>emptyList(), false); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } + + @Test + public void requireThatFrameworkThrowsOnRefreshPackages() throws BundleException, InterruptedException { + OsgiFramework osgi = new NonWorkingOsgiFramework(); + try { + osgi.refreshPackages(); + fail(); + } catch (UnsupportedOperationException e) { + // expected + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestHandlerTestCase.java new file mode 100644 index 00000000000..7893a2d1cf7 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestHandlerTestCase.java @@ -0,0 +1,42 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.test; + +import com.yahoo.jdisc.handler.RequestHandler; +import org.junit.Test; + +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class NonWorkingRequestHandlerTestCase { + + @Test + public void requireThatHandleRequestThrowsException() { + RequestHandler requestHandler = new NonWorkingRequestHandler(); + try { + requestHandler.handleRequest(null, null); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + @Test + public void requireThatHandleTimeoutThrowsException() { + RequestHandler requestHandler = new NonWorkingRequestHandler(); + try { + requestHandler.handleTimeout(null, null); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + @Test + public void requireThatDestroyDoesNotThrow() { + RequestHandler requestHandler = new NonWorkingRequestHandler(); + requestHandler.release(); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestTestCase.java new file mode 100644 index 00000000000..0d1b33fa72f --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingRequestTestCase.java @@ -0,0 +1,35 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.test; + +import com.google.inject.AbstractModule; +import com.google.inject.Key; +import com.google.inject.name.Names; +import com.yahoo.jdisc.Request; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class NonWorkingRequestTestCase { + + @Test + public void requireThatFactoryMethodWorks() { + assertNotNull(NonWorkingRequest.newInstance("scheme://host/path")); + } + + @Test + public void requireThatGuiceModulesAreInjected() { + Request request = NonWorkingRequest.newInstance("scheme://host/path", new AbstractModule() { + + @Override + protected void configure() { + bind(String.class).annotatedWith(Names.named("foo")).toInstance("bar"); + } + }); + assertEquals("bar", request.container().getInstance(Key.get(String.class, Names.named("foo")))); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingResponseHandlerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingResponseHandlerTestCase.java new file mode 100644 index 00000000000..fb76e66e1e1 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingResponseHandlerTestCase.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.jdisc.test; + +import com.yahoo.jdisc.Response; +import org.junit.Test; + +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class NonWorkingResponseHandlerTestCase { + + @Test + public void requireThatHandleResponseThrowsException() { + NonWorkingResponseHandler handler = new NonWorkingResponseHandler(); + try { + handler.handleResponse(new Response(Response.Status.OK)); + fail(); + } catch (UnsupportedOperationException e) { + + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingServerTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingServerTestCase.java new file mode 100644 index 00000000000..eccf4dbc655 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/NonWorkingServerTestCase.java @@ -0,0 +1,35 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.test; + +import com.yahoo.jdisc.service.ServerProvider; +import org.junit.Test; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class NonWorkingServerTestCase { + + @Test + public void requireThatStartDoesNotThrow() { + ServerProvider server = new NonWorkingServerProvider(); + server.start(); + } + + @Test + public void requireThatCloseDoesNotThrow() { + ServerProvider server = new NonWorkingServerProvider(); + server.close(); + } + + @Test + public void requireThatReferDoesNotThrow() { + ServerProvider server = new NonWorkingServerProvider(); + server.refer(); + } + + @Test + public void requireThatReleaseDoesNotThrow() { + ServerProvider server = new NonWorkingServerProvider(); + server.release(); + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/ServerProviderConformanceTestTest.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/ServerProviderConformanceTestTest.java new file mode 100644 index 00000000000..e59f8c96100 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/ServerProviderConformanceTestTest.java @@ -0,0 +1,657 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.test; + +import com.google.common.util.concurrent.SettableFuture; +import com.google.inject.Inject; +import com.google.inject.Module; +import com.google.inject.util.Modules; +import com.yahoo.jdisc.NoopSharedResource; +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.service.CurrentContainer; +import com.yahoo.jdisc.service.ServerProvider; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + */ +public class ServerProviderConformanceTestTest extends ServerProviderConformanceTest { + + @Override + @Test + public void testContainerNotReadyException() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testBindingSetNotFoundException() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testNoBindingSetSelectedException() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testBindingNotFoundException() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestHandlerWithSyncCloseResponse() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestHandlerWithSyncWriteResponse() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestHandlerWithSyncHandleResponse() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestHandlerWithAsyncHandleResponse() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestException() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestExceptionWithSyncCloseResponse() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestExceptionWithSyncWriteResponse() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestNondeterministicExceptionWithSyncHandleResponse() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestExceptionBeforeResponseWriteWithSyncHandleResponse() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestExceptionAfterResponseWriteWithSyncHandleResponse() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestNondeterministicExceptionWithAsyncHandleResponse() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestExceptionBeforeResponseWriteWithAsyncHandleResponse() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestExceptionAfterResponseCloseNoContentWithAsyncHandleResponse() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestExceptionAfterResponseWriteWithAsyncHandleResponse() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteWithSyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteWithAsyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteWithNondeterministicSyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteWithSyncFailureBeforeResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteWithSyncFailureAfterResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteWithNondeterministicAsyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteWithAsyncFailureBeforeResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteWithAsyncFailureAfterResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteWithAsyncFailureAfterResponseCloseNoContent() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteNondeterministicException() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionBeforeResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionAfterResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionAfterResponseCloseNoContent() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteNondeterministicExceptionWithSyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionBeforeResponseWriteWithSyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionAfterResponseWriteWithSyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionAfterResponseCloseNoContentWithSyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteNondeterministicExceptionWithAsyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionBeforeResponseWriteWithAsyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionAfterResponseWriteWithAsyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionAfterResponseCloseNoContentWithAsyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionWithNondeterministicSyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionWithSyncFailureBeforeResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionWithSyncFailureAfterResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionWithSyncFailureAfterResponseCloseNoContent() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionWithNondeterministicAsyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionWithAsyncFailureBeforeResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionWithAsyncFailureAfterResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentWriteExceptionWithAsyncFailureAfterResponseCloseNoContent() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseWithSyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseWithAsyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseWithNondeterministicSyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseWithSyncFailureBeforeResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseWithSyncFailureAfterResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseWithSyncFailureAfterResponseCloseNoContent() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseWithNondeterministicAsyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseWithAsyncFailureBeforeResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseWithAsyncFailureAfterResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseWithAsyncFailureAfterResponseCloseNoContent() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseNondeterministicException() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionBeforeResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionAfterResponseWrite() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionAfterResponseCloseNoContent() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseNondeterministicExceptionWithSyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionBeforeResponseWriteWithSyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionAfterResponseWriteWithSyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionAfterResponseCloseNoContentWithSyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseNondeterministicExceptionWithAsyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionBeforeResponseWriteWithAsyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionAfterResponseWriteWithAsyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionAfterResponseCloseNoContentWithAsyncCompletion() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseNondeterministicExceptionWithSyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionBeforeResponseWriteWithSyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionAfterResponseWriteWithSyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionAfterResponseCloseNoContentWithSyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseNondeterministicExceptionWithAsyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionBeforeResponseWriteWithAsyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionAfterResponseWriteWithAsyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testRequestContentCloseExceptionAfterResponseCloseNoContentWithAsyncFailure() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testResponseWriteCompletionException() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testResponseCloseCompletionException() throws Throwable { + runTest(new MyAdapter()); + } + + @Override + @Test + public void testResponseCloseCompletionExceptionNoContent() throws Throwable { + runTest(new MyAdapter()); + } + + private static void tryWrite(final ContentChannel out, final String str) { + try { + out.write(StandardCharsets.UTF_8.encode(str), null); + } catch (Throwable t) { + // Simulate handling the failure. + t.getMessage(); + } + } + + private static void tryClose(final ContentChannel out) { + try { + out.close(null); + } catch (Throwable t) { + // Simulate handling the failure. + t.getMessage(); + } + } + + private static void tryComplete(final CompletionHandler handler) { + try { + handler.completed(); + } catch (Throwable t) { + // Simulate handling the failure. + t.getMessage(); + } + } + + private static class MyServer extends NoopSharedResource implements ServerProvider { + + final CurrentContainer container; + + @Inject + MyServer(final CurrentContainer container) { + this.container = container; + } + + @Override + public void start() { + + } + + @Override + public void close() { + + } + } + + private static class MyClient { + + final MyServer server; + + MyClient(final MyServer server) { + this.server = server; + } + + MyResponseHandler executeRequest(final boolean withRequestContent) + throws InterruptedException, ExecutionException, TimeoutException { + final MyResponseHandler responseHandler = new MyResponseHandler(); + final Request request; + try { + request = new Request(server.container, URI.create("http://localhost/")); + } catch (Throwable t) { + responseHandler.response.set(new Response(Response.Status.INTERNAL_SERVER_ERROR, t)); + return responseHandler; + } + try { + final ContentChannel out = request.connect(responseHandler); + if (withRequestContent) { + tryWrite(out, "myRequestContent"); + } + tryClose(out); + } catch (Throwable t) { + responseHandler.response.set(new Response(Response.Status.INTERNAL_SERVER_ERROR, t)); + // Simulate handling the failure. + t.getMessage(); + return responseHandler; + } finally { + request.release(); + } + return responseHandler; + } + } + + private static class MyResponseHandler implements ResponseHandler { + + final SettableFuture<Response> response = SettableFuture.create(); + final SettableFuture<String> content = SettableFuture.create(); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + + @Override + public ContentChannel handleResponse(final Response response) { + this.response.set(response); + return new ContentChannel() { + + @Override + public void write(final ByteBuffer buf, final CompletionHandler handler) { + while (buf.hasRemaining()) { + out.write(buf.get()); + } + tryComplete(handler); + } + + @Override + public void close(final CompletionHandler handler) { + content.set(new String(out.toByteArray(), StandardCharsets.UTF_8)); + tryComplete(handler); + } + }; + } + } + + private static class MyAdapter implements Adapter<MyServer, MyClient, MyResponseHandler> { + + @Override + public Module newConfigModule() { + return Modules.EMPTY_MODULE; + } + + @Override + public Class<MyServer> getServerProviderClass() { + return MyServer.class; + } + + @Override + public MyClient newClient(final MyServer server) throws Throwable { + return new MyClient(server); + } + + @Override + public MyResponseHandler executeRequest( + final MyClient client, + final boolean withRequestContent) throws Throwable { + return client.executeRequest(withRequestContent); + } + + @Override + public Iterable<ByteBuffer> newResponseContent() { + return Collections.singleton(StandardCharsets.UTF_8.encode("myResponseContent")); + } + + @Override + public void validateResponse(final MyResponseHandler responseHandler) throws Throwable { + responseHandler.response.get(600, TimeUnit.SECONDS); + } + } +} diff --git a/jdisc_core/src/test/java/com/yahoo/jdisc/test/TestDriverTestCase.java b/jdisc_core/src/test/java/com/yahoo/jdisc/test/TestDriverTestCase.java new file mode 100644 index 00000000000..bea231d19d9 --- /dev/null +++ b/jdisc_core/src/test/java/com/yahoo/jdisc/test/TestDriverTestCase.java @@ -0,0 +1,163 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.test; + +import com.yahoo.jdisc.Request; +import com.yahoo.jdisc.Response; +import com.yahoo.jdisc.application.Application; +import com.yahoo.jdisc.application.ContainerBuilder; +import com.yahoo.jdisc.handler.AbstractRequestHandler; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.RequestDeniedException; +import com.yahoo.jdisc.handler.ResponseHandler; +import com.yahoo.jdisc.service.ContainerNotReadyException; +import org.junit.Test; + +import java.nio.ByteBuffer; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + */ +public class TestDriverTestCase { + + @Test + public void requireThatFactoryMethodsWork() { + TestDriver.newInjectedApplicationInstance(MyApplication.class).close(); + TestDriver.newInjectedApplicationInstanceWithoutOsgi(MyApplication.class).close(); + TestDriver.newInjectedApplicationInstance(new MyApplication()).close(); + TestDriver.newInjectedApplicationInstanceWithoutOsgi(new MyApplication()).close(); + TestDriver.newSimpleApplicationInstance().close(); + TestDriver.newSimpleApplicationInstanceWithoutOsgi().close(); + } + + @Test + public void requireThatAccessorsWork() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + assertNotNull(driver.bootstrapLoader()); + assertTrue(driver.close()); + } + + @Test + public void requireThatConnectRequestWorks() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = new MyRequestHandler(new MyContentChannel()); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind("scheme://host/path", requestHandler); + driver.activateContainer(builder); + ContentChannel content = driver.connectRequest("scheme://host/path", new MyResponseHandler()); + assertNotNull(content); + content.close(null); + assertNotNull(requestHandler.handler); + requestHandler.handler.handleResponse(new Response(Response.Status.OK)).close(null); + assertTrue(driver.close()); + } + + @Test + public void requireThatDispatchRequestWorks() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + MyRequestHandler requestHandler = new MyRequestHandler(new MyContentChannel()); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind("scheme://host/path", requestHandler); + driver.activateContainer(builder); + driver.dispatchRequest("scheme://host/path", new MyResponseHandler()); + assertNotNull(requestHandler.handler); + assertTrue(requestHandler.content.closed); + requestHandler.handler.handleResponse(new Response(Response.Status.OK)).close(null); + assertTrue(driver.close()); + } + + @Test + public void requireThatFailedRequestCreateDoesNotBlockClose() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + try { + driver.connectRequest("scheme://host/path", new MyResponseHandler()); + fail(); + } catch (ContainerNotReadyException e) { + + } + assertTrue(driver.close()); + } + + @Test + public void requireThatFailedRequestConnectDoesNotBlockClose() { + TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(); + ContainerBuilder builder = driver.newContainerBuilder(); + builder.serverBindings().bind("scheme://host/path", new MyRequestHandler(null)); + driver.activateContainer(builder); + try { + driver.connectRequest("scheme://host/path", new MyResponseHandler()); + fail(); + } catch (RequestDeniedException e) { + + } + assertTrue(driver.close()); + } + + private static class MyApplication implements Application { + + @Override + public void start() { + + } + + @Override + public void stop() { + + } + + @Override + public void destroy() { + + } + } + + private static class MyRequestHandler extends AbstractRequestHandler { + + final MyContentChannel content; + ResponseHandler handler; + + MyRequestHandler(MyContentChannel content) { + this.content = content; + } + + @Override + public ContentChannel handleRequest(Request request, ResponseHandler handler) { + this.handler = handler; + if (content == null) { + throw new RequestDeniedException(request); + } + return content; + } + } + + private static class MyResponseHandler implements ResponseHandler { + + final MyContentChannel content = new MyContentChannel(); + + @Override + public ContentChannel handleResponse(Response response) { + return content; + } + } + + private static class MyContentChannel implements ContentChannel { + + boolean closed = false; + + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + throw new UnsupportedOperationException(); + } + + @Override + public void close(CompletionHandler handler) { + closed = true; + handler.completed(); + } + } +} diff --git a/jdisc_core/src/test/perl/help.Levent.expected b/jdisc_core/src/test/perl/help.Levent.expected new file mode 100644 index 00000000000..b35e2d9c36b --- /dev/null +++ b/jdisc_core/src/test/perl/help.Levent.expected @@ -0,0 +1,20 @@ +Usage: jdisc_logfmt [options] [inputfile ...] +Options: + -l LEVELLIST --level=LEVELLIST select levels to include + -L LEVELLIST --add-level=LEVELLIST define extra levels + -s FIELDLIST --show=FIELDLIST select fields to print + -p PID --pid=PID select messages from given PID + -S SERVICE --service=SERVICE select messages from given SERVICE + -H HOST --host=HOST select messages from given HOST + -c REGEX --component=REGEX select components matching REGEX + -m REGEX --message=REGEX select message text matching REGEX + -f --follow invoke tail -F to follow input file + -N --nldequote dequote newlines in message text field + -t --tc --truncatecomponent chop component to 15 chars + --ts --truncateservice chop service to 9 chars + +FIELDLIST is comma separated, available fields: + time fmttime msecs usecs host level pid service component message +Available levels for LEVELLIST: + debug error event info unknown warning +for both lists, use 'all' for all possible values, and -xxx to disable xxx. diff --git a/jdisc_core/src/test/perl/help.expected b/jdisc_core/src/test/perl/help.expected new file mode 100644 index 00000000000..58da8183696 --- /dev/null +++ b/jdisc_core/src/test/perl/help.expected @@ -0,0 +1,20 @@ +Usage: jdisc_logfmt [options] [inputfile ...] +Options: + -l LEVELLIST --level=LEVELLIST select levels to include + -L LEVELLIST --add-level=LEVELLIST define extra levels + -s FIELDLIST --show=FIELDLIST select fields to print + -p PID --pid=PID select messages from given PID + -S SERVICE --service=SERVICE select messages from given SERVICE + -H HOST --host=HOST select messages from given HOST + -c REGEX --component=REGEX select components matching REGEX + -m REGEX --message=REGEX select message text matching REGEX + -f --follow invoke tail -F to follow input file + -N --nldequote dequote newlines in message text field + -t --tc --truncatecomponent chop component to 15 chars + --ts --truncateservice chop service to 9 chars + +FIELDLIST is comma separated, available fields: + time fmttime msecs usecs host level pid service component message +Available levels for LEVELLIST: + debug error info unknown warning +for both lists, use 'all' for all possible values, and -xxx to disable xxx. diff --git a/jdisc_core/src/test/perl/jdisc.expected b/jdisc_core/src/test/perl/jdisc.expected new file mode 100644 index 00000000000..6c5d139c0cf --- /dev/null +++ b/jdisc_core/src/test/perl/jdisc.expected @@ -0,0 +1,17 @@ +[2013-01-25 12:46:51.180] INFO : - org.apache.felix.framework ServiceEvent REGISTERED +[2013-01-25 12:46:51.192] INFO : - jdisc_core.app-a BundleEvent INSTALLED +[2013-01-25 12:46:51.249] INFO : - jdisc_core.app-a BundleEvent RESOLVED +[2013-01-25 12:46:51.249] INFO : - jdisc_core.app-a BundleEvent STARTED +[2013-01-25 12:46:51.334] INFO : - jdisc_core.app-a BundleEvent STOPPED +[2013-01-25 12:46:51.335] INFO : - jdisc_core.app-a BundleEvent UNRESOLVED +[2013-01-25 12:46:51.335] INFO : - jdisc_core.app-a BundleEvent UNINSTALLED +[2013-01-25 12:46:51.376] INFO : - org.apache.felix.framework ServiceEvent REGISTERED +[2013-01-25 12:46:51.377] ERROR : - jdisc_core.app-a my_error +[2013-01-25 12:46:51.377] WARNING : - jdisc_core.app-a my_warning +[2013-01-25 12:46:51.377] INFO : - jdisc_core.app-a my_info +[2013-01-25 12:46:51.379] INFO : - jdisc_core.app-a BundleEvent INSTALLED +[2013-01-25 12:46:51.383] INFO : - jdisc_core.app-a BundleEvent RESOLVED +[2013-01-25 12:46:51.383] INFO : - jdisc_core.app-a BundleEvent STARTED +[2013-01-25 12:46:51.389] INFO : - jdisc_core.app-a BundleEvent STOPPED +[2013-01-25 12:46:51.389] INFO : - jdisc_core.app-a BundleEvent UNRESOLVED +[2013-01-25 12:46:51.390] INFO : - jdisc_core.app-a BundleEvent UNINSTALLED diff --git a/jdisc_core/src/test/perl/jdisc.lall.expected b/jdisc_core/src/test/perl/jdisc.lall.expected new file mode 100644 index 00000000000..58875bba8db --- /dev/null +++ b/jdisc_core/src/test/perl/jdisc.lall.expected @@ -0,0 +1,19 @@ +[2013-01-25 12:46:51.180] INFO : - org.apache.felix.framework ServiceEvent REGISTERED +[2013-01-25 12:46:51.192] INFO : - jdisc_core.app-a BundleEvent INSTALLED +[2013-01-25 12:46:51.249] INFO : - jdisc_core.app-a BundleEvent RESOLVED +[2013-01-25 12:46:51.249] INFO : - jdisc_core.app-a BundleEvent STARTED +[2013-01-25 12:46:51.334] INFO : - jdisc_core.app-a BundleEvent STOPPED +[2013-01-25 12:46:51.335] INFO : - jdisc_core.app-a BundleEvent UNRESOLVED +[2013-01-25 12:46:51.335] INFO : - jdisc_core.app-a BundleEvent UNINSTALLED +[2013-01-25 12:46:51.376] INFO : - org.apache.felix.framework ServiceEvent REGISTERED +[2013-01-25 12:46:51.377] ERROR : - jdisc_core.app-a my_error +[2013-01-25 12:46:51.377] WARNING : - jdisc_core.app-a my_warning +[2013-01-25 12:46:51.377] INFO : - jdisc_core.app-a my_info +[2013-01-25 12:46:51.377] DEBUG : - jdisc_core.app-a my_debug +[2013-01-25 12:46:51.377] UNKNOWN : - jdisc_core.app-a my_unknown +[2013-01-25 12:46:51.379] INFO : - jdisc_core.app-a BundleEvent INSTALLED +[2013-01-25 12:46:51.383] INFO : - jdisc_core.app-a BundleEvent RESOLVED +[2013-01-25 12:46:51.383] INFO : - jdisc_core.app-a BundleEvent STARTED +[2013-01-25 12:46:51.389] INFO : - jdisc_core.app-a BundleEvent STOPPED +[2013-01-25 12:46:51.389] INFO : - jdisc_core.app-a BundleEvent UNRESOLVED +[2013-01-25 12:46:51.390] INFO : - jdisc_core.app-a BundleEvent UNINSTALLED diff --git a/jdisc_core/src/test/perl/jdisc.lall_info.expected b/jdisc_core/src/test/perl/jdisc.lall_info.expected new file mode 100644 index 00000000000..da834dddc9b --- /dev/null +++ b/jdisc_core/src/test/perl/jdisc.lall_info.expected @@ -0,0 +1,4 @@ +[2013-01-25 12:46:51.377] ERROR : - jdisc_core.app-a my_error +[2013-01-25 12:46:51.377] WARNING : - jdisc_core.app-a my_warning +[2013-01-25 12:46:51.377] DEBUG : - jdisc_core.app-a my_debug +[2013-01-25 12:46:51.377] UNKNOWN : - jdisc_core.app-a my_unknown diff --git a/jdisc_core/src/test/perl/jdisc.log b/jdisc_core/src/test/perl/jdisc.log new file mode 100644 index 00000000000..37a74a595ca --- /dev/null +++ b/jdisc_core/src/test/perl/jdisc.log @@ -0,0 +1,19 @@ +1359114411.180 gentleadd-lm 35172 - org.apache.felix.framework info ServiceEvent REGISTERED +1359114411.192 gentleadd-lm 35172 - jdisc_core.app-a info BundleEvent INSTALLED +1359114411.249 gentleadd-lm 35172 - jdisc_core.app-a info BundleEvent RESOLVED +1359114411.249 gentleadd-lm 35172 - jdisc_core.app-a info BundleEvent STARTED +1359114411.334 gentleadd-lm 35172 - jdisc_core.app-a info BundleEvent STOPPED +1359114411.335 gentleadd-lm 35172 - jdisc_core.app-a info BundleEvent UNRESOLVED +1359114411.335 gentleadd-lm 35172 - jdisc_core.app-a info BundleEvent UNINSTALLED +1359114411.376 gentleadd-lm 35172 - org.apache.felix.framework info ServiceEvent REGISTERED +1359114411.377 gentleadd-lm 35172 - jdisc_core.app-a error my_error +1359114411.377 gentleadd-lm 35172 - jdisc_core.app-a warning my_warning +1359114411.377 gentleadd-lm 35172 - jdisc_core.app-a info my_info +1359114411.377 gentleadd-lm 35172 - jdisc_core.app-a debug my_debug +1359114411.377 gentleadd-lm 35172 - jdisc_core.app-a unknown my_unknown +1359114411.379 gentleadd-lm 35172 - jdisc_core.app-a info BundleEvent INSTALLED +1359114411.383 gentleadd-lm 35172 - jdisc_core.app-a info BundleEvent RESOLVED +1359114411.383 gentleadd-lm 35172 - jdisc_core.app-a info BundleEvent STARTED +1359114411.389 gentleadd-lm 35172 - jdisc_core.app-a info BundleEvent STOPPED +1359114411.389 gentleadd-lm 35172 - jdisc_core.app-a info BundleEvent UNRESOLVED +1359114411.390 gentleadd-lm 35172 - jdisc_core.app-a info BundleEvent UNINSTALLED diff --git a/jdisc_core/src/test/perl/jdisc.spid.expected b/jdisc_core/src/test/perl/jdisc.spid.expected new file mode 100644 index 00000000000..44963161dcf --- /dev/null +++ b/jdisc_core/src/test/perl/jdisc.spid.expected @@ -0,0 +1,17 @@ +[2013-01-25 12:46:51.180] INFO : 35172 - org.apache.felix.framework ServiceEvent REGISTERED +[2013-01-25 12:46:51.192] INFO : 35172 - jdisc_core.app-a BundleEvent INSTALLED +[2013-01-25 12:46:51.249] INFO : 35172 - jdisc_core.app-a BundleEvent RESOLVED +[2013-01-25 12:46:51.249] INFO : 35172 - jdisc_core.app-a BundleEvent STARTED +[2013-01-25 12:46:51.334] INFO : 35172 - jdisc_core.app-a BundleEvent STOPPED +[2013-01-25 12:46:51.335] INFO : 35172 - jdisc_core.app-a BundleEvent UNRESOLVED +[2013-01-25 12:46:51.335] INFO : 35172 - jdisc_core.app-a BundleEvent UNINSTALLED +[2013-01-25 12:46:51.376] INFO : 35172 - org.apache.felix.framework ServiceEvent REGISTERED +[2013-01-25 12:46:51.377] ERROR : 35172 - jdisc_core.app-a my_error +[2013-01-25 12:46:51.377] WARNING : 35172 - jdisc_core.app-a my_warning +[2013-01-25 12:46:51.377] INFO : 35172 - jdisc_core.app-a my_info +[2013-01-25 12:46:51.379] INFO : 35172 - jdisc_core.app-a BundleEvent INSTALLED +[2013-01-25 12:46:51.383] INFO : 35172 - jdisc_core.app-a BundleEvent RESOLVED +[2013-01-25 12:46:51.383] INFO : 35172 - jdisc_core.app-a BundleEvent STARTED +[2013-01-25 12:46:51.389] INFO : 35172 - jdisc_core.app-a BundleEvent STOPPED +[2013-01-25 12:46:51.389] INFO : 35172 - jdisc_core.app-a BundleEvent UNRESOLVED +[2013-01-25 12:46:51.390] INFO : 35172 - jdisc_core.app-a BundleEvent UNINSTALLED diff --git a/jdisc_core/src/test/perl/jdisc_logfmt_test.sh b/jdisc_core/src/test/perl/jdisc_logfmt_test.sh new file mode 100755 index 00000000000..bb7e92ed8cf --- /dev/null +++ b/jdisc_core/src/test/perl/jdisc_logfmt_test.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +MYPATH=`dirname ${0}` +DIFF=/usr/bin/diff +LOGFMT=${1} + +if [ -e "/usr/local/bin/perl" ]; then + echo "Running jdisc_logfmt test suite." +else + echo "Ignoring jdisc_logfmt test suite as there is no /usr/local/bin/perl" + exit 0 +fi + +set -e +export TZ=CET +export VESPA_HOME=$(mktemp -d /tmp/mockup-vespahome-XXXXXX)/ +mkdir -p $VESPA_HOME/libexec/vespa +touch $VESPA_HOME/libexec/vespa/common-env.sh + +echo + +${LOGFMT} -h 2>&1 | ${DIFF} - ${MYPATH}/help.expected +${LOGFMT} -h -L event 2>&1 | ${DIFF} - ${MYPATH}/help.Levent.expected + +${LOGFMT} ${MYPATH}/jdisc.log 2>&1 | ${DIFF} - ${MYPATH}/jdisc.expected +${LOGFMT} -l all ${MYPATH}/jdisc.log 2>&1 | ${DIFF} - ${MYPATH}/jdisc.lall.expected +${LOGFMT} -l all,-info ${MYPATH}/jdisc.log 2>&1 | ${DIFF} - ${MYPATH}/jdisc.lall_info.expected +${LOGFMT} -s +pid ${MYPATH}/jdisc.log 2>&1 | ${DIFF} - ${MYPATH}/jdisc.spid.expected + +${LOGFMT} ${MYPATH}/vespa.log 2>&1 | ${DIFF} - ${MYPATH}/vespa.expected +${LOGFMT} -L event ${MYPATH}/vespa.log 2>&1 | ${DIFF} - ${MYPATH}/vespa.Levent.expected +${LOGFMT} -L event -l all ${MYPATH}/vespa.log 2>&1 | ${DIFF} - ${MYPATH}/vespa.Levent.lall.expected + +rm -r ${VESPA_HOME} +echo All tests passed. diff --git a/jdisc_core/src/test/perl/vespa.Levent.expected b/jdisc_core/src/test/perl/vespa.Levent.expected new file mode 100644 index 00000000000..334ba5f5b28 --- /dev/null +++ b/jdisc_core/src/test/perl/vespa.Levent.expected @@ -0,0 +1,9 @@ +[2012-11-27 14:22:48.120] INFO : configserver stdout ROOT = /home/vespa +[2012-11-27 14:22:48.232] INFO : configserver stdout Running without a pid file. +[2012-11-27 14:22:48.336] INFO : configserver stdout LANG = en_US.UTF-8 +[2012-11-27 14:22:48.393] INFO : configserver stdout env LD_PRELOAD=/home/vespa/libexec64/yjava_daemon_preload.so: /home/vespa/bin64/yjava_daemon -sentinel -rlimit_files 16384 -logdest /home/vespa/logs/vespa/yjava_daemon.out -loglevel error -jvm server -maxrestarts 0 -procs 1 -home /home/vespa/share/yjava_jdk/java -Djava.library.path=/home/vespa/lib64:/home/vespa/lib64 -Dvespa.log.control.dir=/home/vespa/var/db/vespa/logcontrol -XX:ThreadStackSize=512 -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=128 -XX:MaxPermSize=512m -Dconfig.id=dir:/home/vespa/conf/configserver -Dyjava_remote_ip_servlet_filter.logLevel=DEBUG -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger -Dcom.yahoo.protect.Process.forcedExitActive=true -Dzookeeperlogfile=/home/vespa/logs/vespa/zookeeper.log -Xms1536m -Xmx1536m -XX:MaxDirectMemorySize=267m -Djava.awt.headless=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/vespa/var/crash -Dsun.net.client.defaultConnectTimeout=5000 -Dsun.net.client.defaultReadTimeout=60000 -Djavax.net.ssl.keyStoreType=JKS -javaagent:/home/vespa/lib/jars/yjava_ysecure_agent.jar -Djdisc_core.config.file=/home/vespa/var/jdisc_core/config.properties -Djdisc.export.packages=yjava.security.ysecure,yjava.security.yca,yjava.security.yck,org.apache.velocity,org.apache.velocity.app,org.apache.velocity.context,org.apache.velocity.runtime,org.apache.velocity.runtime.log, -Djdisc.cache.path=/home/vespa/var/vespa/bundlecache/dir:_home_y_conf_configserver -Djdisc.debug.resources=false -Djdisc.bundle.path=/home/vespa/lib/jars -Djdisc.logger.enabled=false -Djdisc.logger.level=ALL -Djdisc.logger.tag=dir:/home/vespa/conf/configserver -user yahoo -cp /home/vespa/lib/jars/jdisc_core-with-dependencies.jar:lib/jars/yjava_bcookie.jar:lib/jars/yjava_bcookie_jni.jar:lib/jars/yjava_byauth.jar:lib/jars/yjava_cookie_data_servlet_filter.jar:lib/jars/yjava_daemon.jar:lib/jars/yjava_jmx_singleton_server.jar:lib/jars/yjava_remote_ip_servlet_filter.jar:lib/jars/yjava_resource_handler.jar:lib/jars/yjava_servlet.jar:lib/jars/yjava_servlet_filters.jar:lib/jars/yjava_yca.jar:lib/jars/yjava_yck.jar:lib/jars/yjava_yhdrs:lib/jars/yjava_yiv.jar:lib/jars/yjava_yiv_servlet.jar:lib/jars/yjava_ynet.jar:lib/jars/yjava_ysecure.jar:lib/jars/yjava_ysecure_agent.jar:lib/jars/yjava_ysecure_native.jar:share/jports/org_apache_velocity__velocity.jar:share/jports/commons_collections__commons_collections.jar:share/jports/commons_lang__commons_lang.jar: -ynet FILTER_YAHOO_ANY --wait-for-jvm-init 10 -Dzookeeper.jmx.log4j.disable=true -Dconfigsources=tcp/localhost:19070 -XX:OnOutOfMemoryError="kill -9 %p" com.yahoo.jdisc.core.BootstrapDaemon file:/home/vespa/lib/jars/container-disc-with-dependencies.jar +[2012-11-27 14:22:50.876] INFO : configserver Container.com.yahoo.container.handler.config.HandlersConfigurerDi Installing bundles from the latest application +[2012-11-27 14:22:50.884] INFO : configserver Container.com.yahoo.container.handler.BundleLoader Installing bundle from disk with reference 'file:/home/vespa/lib/jars/config-models/sherpa-config-model-plugin.jar' +[2012-11-27 14:22:50.889] INFO : configserver Container.com.yahoo.container.handler.BundleLoader Installing bundle from disk with reference 'file:/home/vespa/lib/jars/configserver-container-plugin.jar' +[2012-11-27 14:22:54.285] INFO : configserver Container.com.yahoo.container.handler.config.HandlersConfigurerDi Installing bundles from the latest application +[2012-11-27 14:22:57.489] INFO : configserver Container.com.yahoo.container.handler.config.HandlersConfigurerDi Switched to the latest deployed set of handlers, and dependent components, e.g. search chains, searchers and document processors. Application switch number: 0 diff --git a/jdisc_core/src/test/perl/vespa.Levent.lall.expected b/jdisc_core/src/test/perl/vespa.Levent.lall.expected new file mode 100644 index 00000000000..ef58eee7bfc --- /dev/null +++ b/jdisc_core/src/test/perl/vespa.Levent.lall.expected @@ -0,0 +1,19 @@ +[2012-11-27 14:22:48.091] EVENT : configserver runserver starting/1 name="/home/vespa/bin/vespa-start-container-daemon -Dzookeeper.jmx.log4j.disable=true -Dconfigsources=tcp/localhost:19070 (pid 18151)" +[2012-11-27 14:22:48.100] DEBUG : configserver qrs-start exporting: YELL_MA_EURO=INXIGHT +[2012-11-27 14:22:48.100] DEBUG : configserver qrs-start Not setting ulimit -v, no limit set. +[2012-11-27 14:22:48.120] INFO : configserver stdout ROOT = /home/vespa +[2012-11-27 14:22:48.232] INFO : configserver stdout Running without a pid file. +[2012-11-27 14:22:48.336] INFO : configserver stdout LANG = en_US.UTF-8 +[2012-11-27 14:22:48.393] INFO : configserver stdout env LD_PRELOAD=/home/vespa/libexec64/yjava_daemon_preload.so: /home/vespa/bin64/yjava_daemon -sentinel -rlimit_files 16384 -logdest /home/vespa/logs/vespa/yjava_daemon.out -loglevel error -jvm server -maxrestarts 0 -procs 1 -home /home/vespa/share/yjava_jdk/java -Djava.library.path=/home/vespa/lib64:/home/vespa/lib64 -Dvespa.log.control.dir=/home/vespa/var/db/vespa/logcontrol -XX:ThreadStackSize=512 -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=128 -XX:MaxPermSize=512m -Dconfig.id=dir:/home/vespa/conf/configserver -Dyjava_remote_ip_servlet_filter.logLevel=DEBUG -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger -Dcom.yahoo.protect.Process.forcedExitActive=true -Dzookeeperlogfile=/home/vespa/logs/vespa/zookeeper.log -Xms1536m -Xmx1536m -XX:MaxDirectMemorySize=267m -Djava.awt.headless=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/vespa/var/crash -Dsun.net.client.defaultConnectTimeout=5000 -Dsun.net.client.defaultReadTimeout=60000 -Djavax.net.ssl.keyStoreType=JKS -javaagent:/home/vespa/lib/jars/yjava_ysecure_agent.jar -Djdisc_core.config.file=/home/vespa/var/jdisc_core/config.properties -Djdisc.export.packages=yjava.security.ysecure,yjava.security.yca,yjava.security.yck,org.apache.velocity,org.apache.velocity.app,org.apache.velocity.context,org.apache.velocity.runtime,org.apache.velocity.runtime.log, -Djdisc.cache.path=/home/vespa/var/vespa/bundlecache/dir:_home_y_conf_configserver -Djdisc.debug.resources=false -Djdisc.bundle.path=/home/vespa/lib/jars -Djdisc.logger.enabled=false -Djdisc.logger.level=ALL -Djdisc.logger.tag=dir:/home/vespa/conf/configserver -user yahoo -cp /home/vespa/lib/jars/jdisc_core-with-dependencies.jar:lib/jars/yjava_bcookie.jar:lib/jars/yjava_bcookie_jni.jar:lib/jars/yjava_byauth.jar:lib/jars/yjava_cookie_data_servlet_filter.jar:lib/jars/yjava_daemon.jar:lib/jars/yjava_jmx_singleton_server.jar:lib/jars/yjava_remote_ip_servlet_filter.jar:lib/jars/yjava_resource_handler.jar:lib/jars/yjava_servlet.jar:lib/jars/yjava_servlet_filters.jar:lib/jars/yjava_yca.jar:lib/jars/yjava_yck.jar:lib/jars/yjava_yhdrs:lib/jars/yjava_yiv.jar:lib/jars/yjava_yiv_servlet.jar:lib/jars/yjava_ynet.jar:lib/jars/yjava_ysecure.jar:lib/jars/yjava_ysecure_agent.jar:lib/jars/yjava_ysecure_native.jar:share/jports/org_apache_velocity__velocity.jar:share/jports/commons_collections__commons_collections.jar:share/jports/commons_lang__commons_lang.jar: -ynet FILTER_YAHOO_ANY --wait-for-jvm-init 10 -Dzookeeper.jmx.log4j.disable=true -Dconfigsources=tcp/localhost:19070 -XX:OnOutOfMemoryError="kill -9 %p" com.yahoo.jdisc.core.BootstrapDaemon file:/home/vespa/lib/jars/container-disc-with-dependencies.jar +[2012-11-27 14:22:50.876] INFO : configserver Container.com.yahoo.container.handler.config.HandlersConfigurerDi Installing bundles from the latest application +[2012-11-27 14:22:50.884] INFO : configserver Container.com.yahoo.container.handler.BundleLoader Installing bundle from disk with reference 'file:/home/vespa/lib/jars/config-models/sherpa-config-model-plugin.jar' +[2012-11-27 14:22:50.889] INFO : configserver Container.com.yahoo.container.handler.BundleLoader Installing bundle from disk with reference 'file:/home/vespa/lib/jars/configserver-container-plugin.jar' +[2012-11-27 14:22:54.285] INFO : configserver Container.com.yahoo.container.handler.config.HandlersConfigurerDi Installing bundles from the latest application +[2012-11-27 14:22:57.430] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=requests value=0 +[2012-11-27 14:22:57.431] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=requestsCached value=0 +[2012-11-27 14:22:57.432] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=requestsNotCached value=0 +[2012-11-27 14:22:57.432] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=failedRequests value=0 +[2012-11-27 14:22:57.432] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=procTime value=0 +[2012-11-27 14:22:57.432] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=procTimeCached value=0 +[2012-11-27 14:22:57.432] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=procTimeNotCached value=0 +[2012-11-27 14:22:57.489] INFO : configserver Container.com.yahoo.container.handler.config.HandlersConfigurerDi Switched to the latest deployed set of handlers, and dependent components, e.g. search chains, searchers and document processors. Application switch number: 0 diff --git a/jdisc_core/src/test/perl/vespa.expected b/jdisc_core/src/test/perl/vespa.expected new file mode 100644 index 00000000000..897a7084dae --- /dev/null +++ b/jdisc_core/src/test/perl/vespa.expected @@ -0,0 +1,18 @@ +Warning: unknown level 'event' in input +[2012-11-27 14:22:48.091] EVENT : configserver runserver starting/1 name="/home/vespa/bin/vespa-start-container-daemon -Dzookeeper.jmx.log4j.disable=true -Dconfigsources=tcp/localhost:19070 (pid 18151)" +[2012-11-27 14:22:48.120] INFO : configserver stdout ROOT = /home/vespa +[2012-11-27 14:22:48.232] INFO : configserver stdout Running without a pid file. +[2012-11-27 14:22:48.336] INFO : configserver stdout LANG = en_US.UTF-8 +[2012-11-27 14:22:48.393] INFO : configserver stdout env LD_PRELOAD=/home/vespa/libexec64/yjava_daemon_preload.so: /home/vespa/bin64/yjava_daemon -sentinel -rlimit_files 16384 -logdest /home/vespa/logs/vespa/yjava_daemon.out -loglevel error -jvm server -maxrestarts 0 -procs 1 -home /home/vespa/share/yjava_jdk/java -Djava.library.path=/home/vespa/lib64:/home/vespa/lib64 -Dvespa.log.control.dir=/home/vespa/var/db/vespa/logcontrol -XX:ThreadStackSize=512 -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=128 -XX:MaxPermSize=512m -Dconfig.id=dir:/home/vespa/conf/configserver -Dyjava_remote_ip_servlet_filter.logLevel=DEBUG -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger -Dcom.yahoo.protect.Process.forcedExitActive=true -Dzookeeperlogfile=/home/vespa/logs/vespa/zookeeper.log -Xms1536m -Xmx1536m -XX:MaxDirectMemorySize=267m -Djava.awt.headless=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/vespa/var/crash -Dsun.net.client.defaultConnectTimeout=5000 -Dsun.net.client.defaultReadTimeout=60000 -Djavax.net.ssl.keyStoreType=JKS -javaagent:/home/vespa/lib/jars/yjava_ysecure_agent.jar -Djdisc_core.config.file=/home/vespa/var/jdisc_core/config.properties -Djdisc.export.packages=yjava.security.ysecure,yjava.security.yca,yjava.security.yck,org.apache.velocity,org.apache.velocity.app,org.apache.velocity.context,org.apache.velocity.runtime,org.apache.velocity.runtime.log, -Djdisc.cache.path=/home/vespa/var/vespa/bundlecache/dir:_home_y_conf_configserver -Djdisc.debug.resources=false -Djdisc.bundle.path=/home/vespa/lib/jars -Djdisc.logger.enabled=false -Djdisc.logger.level=ALL -Djdisc.logger.tag=dir:/home/vespa/conf/configserver -user yahoo -cp /home/vespa/lib/jars/jdisc_core-with-dependencies.jar:lib/jars/yjava_bcookie.jar:lib/jars/yjava_bcookie_jni.jar:lib/jars/yjava_byauth.jar:lib/jars/yjava_cookie_data_servlet_filter.jar:lib/jars/yjava_daemon.jar:lib/jars/yjava_jmx_singleton_server.jar:lib/jars/yjava_remote_ip_servlet_filter.jar:lib/jars/yjava_resource_handler.jar:lib/jars/yjava_servlet.jar:lib/jars/yjava_servlet_filters.jar:lib/jars/yjava_yca.jar:lib/jars/yjava_yck.jar:lib/jars/yjava_yhdrs:lib/jars/yjava_yiv.jar:lib/jars/yjava_yiv_servlet.jar:lib/jars/yjava_ynet.jar:lib/jars/yjava_ysecure.jar:lib/jars/yjava_ysecure_agent.jar:lib/jars/yjava_ysecure_native.jar:share/jports/org_apache_velocity__velocity.jar:share/jports/commons_collections__commons_collections.jar:share/jports/commons_lang__commons_lang.jar: -ynet FILTER_YAHOO_ANY --wait-for-jvm-init 10 -Dzookeeper.jmx.log4j.disable=true -Dconfigsources=tcp/localhost:19070 -XX:OnOutOfMemoryError="kill -9 %p" com.yahoo.jdisc.core.BootstrapDaemon file:/home/vespa/lib/jars/container-disc-with-dependencies.jar +[2012-11-27 14:22:50.876] INFO : configserver Container.com.yahoo.container.handler.config.HandlersConfigurerDi Installing bundles from the latest application +[2012-11-27 14:22:50.884] INFO : configserver Container.com.yahoo.container.handler.BundleLoader Installing bundle from disk with reference 'file:/home/vespa/lib/jars/config-models/sherpa-config-model-plugin.jar' +[2012-11-27 14:22:50.889] INFO : configserver Container.com.yahoo.container.handler.BundleLoader Installing bundle from disk with reference 'file:/home/vespa/lib/jars/configserver-container-plugin.jar' +[2012-11-27 14:22:54.285] INFO : configserver Container.com.yahoo.container.handler.config.HandlersConfigurerDi Installing bundles from the latest application +[2012-11-27 14:22:57.430] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=requests value=0 +[2012-11-27 14:22:57.431] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=requestsCached value=0 +[2012-11-27 14:22:57.432] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=requestsNotCached value=0 +[2012-11-27 14:22:57.432] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=failedRequests value=0 +[2012-11-27 14:22:57.432] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=procTime value=0 +[2012-11-27 14:22:57.432] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=procTimeCached value=0 +[2012-11-27 14:22:57.432] EVENT : configserver Container.com.yahoo.statistics.Counter count/1 name=procTimeNotCached value=0 +[2012-11-27 14:22:57.489] INFO : configserver Container.com.yahoo.container.handler.config.HandlersConfigurerDi Switched to the latest deployed set of handlers, and dependent components, e.g. search chains, searchers and document processors. Application switch number: 0 diff --git a/jdisc_core/src/test/perl/vespa.log b/jdisc_core/src/test/perl/vespa.log new file mode 100644 index 00000000000..36210bdb798 --- /dev/null +++ b/jdisc_core/src/test/perl/vespa.log @@ -0,0 +1,19 @@ +1354022568.091108 example.yahoo.com 18150/38735 configserver runserver event starting/1 name="/home/vespa/bin/vespa-start-container-daemon -Dzookeeper.jmx.log4j.disable=true -Dconfigsources=tcp/localhost:19070 (pid 18151)" +1354022568.100151 example.yahoo.com 18151 configserver qrs-start debug exporting: YELL_MA_EURO=INXIGHT +1354022568.100217 example.yahoo.com 18151 configserver qrs-start debug Not setting ulimit -v, no limit set. +1354022568.120716 example.yahoo.com 18151 configserver stdout info ROOT = /home/vespa +1354022568.232852 example.yahoo.com 18151 configserver stdout info Running without a pid file. +1354022568.336341 example.yahoo.com 18151 configserver stdout info LANG = en_US.UTF-8 +1354022568.393706 example.yahoo.com 18151 configserver stdout info env LD_PRELOAD=/home/vespa/libexec64/yjava_daemon_preload.so: /home/vespa/bin64/yjava_daemon -sentinel -rlimit_files 16384 -logdest /home/vespa/logs/vespa/yjava_daemon.out -loglevel error -jvm server -maxrestarts 0 -procs 1 -home /home/vespa/share/yjava_jdk/java -Djava.library.path=/home/vespa/lib64:/home/vespa/lib64 -Dvespa.log.control.dir=/home/vespa/var/db/vespa/logcontrol -XX:ThreadStackSize=512 -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=128 -XX:MaxPermSize=512m -Dconfig.id=dir:/home/vespa/conf/configserver -Dyjava_remote_ip_servlet_filter.logLevel=DEBUG -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger -Dcom.yahoo.protect.Process.forcedExitActive=true -Dzookeeperlogfile=/home/vespa/logs/vespa/zookeeper.log -Xms1536m -Xmx1536m -XX:MaxDirectMemorySize=267m -Djava.awt.headless=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/vespa/var/crash -Dsun.net.client.defaultConnectTimeout=5000 -Dsun.net.client.defaultReadTimeout=60000 -Djavax.net.ssl.keyStoreType=JKS -javaagent:/home/vespa/lib/jars/yjava_ysecure_agent.jar -Djdisc_core.config.file=/home/vespa/var/jdisc_core/config.properties -Djdisc.export.packages=yjava.security.ysecure,yjava.security.yca,yjava.security.yck,org.apache.velocity,org.apache.velocity.app,org.apache.velocity.context,org.apache.velocity.runtime,org.apache.velocity.runtime.log, -Djdisc.cache.path=/home/vespa/var/vespa/bundlecache/dir:_home_y_conf_configserver -Djdisc.debug.resources=false -Djdisc.bundle.path=/home/vespa/lib/jars -Djdisc.logger.enabled=false -Djdisc.logger.level=ALL -Djdisc.logger.tag=dir:/home/vespa/conf/configserver -user yahoo -cp /home/vespa/lib/jars/jdisc_core-with-dependencies.jar:lib/jars/yjava_bcookie.jar:lib/jars/yjava_bcookie_jni.jar:lib/jars/yjava_byauth.jar:lib/jars/yjava_cookie_data_servlet_filter.jar:lib/jars/yjava_daemon.jar:lib/jars/yjava_jmx_singleton_server.jar:lib/jars/yjava_remote_ip_servlet_filter.jar:lib/jars/yjava_resource_handler.jar:lib/jars/yjava_servlet.jar:lib/jars/yjava_servlet_filters.jar:lib/jars/yjava_yca.jar:lib/jars/yjava_yck.jar:lib/jars/yjava_yhdrs:lib/jars/yjava_yiv.jar:lib/jars/yjava_yiv_servlet.jar:lib/jars/yjava_ynet.jar:lib/jars/yjava_ysecure.jar:lib/jars/yjava_ysecure_agent.jar:lib/jars/yjava_ysecure_native.jar:share/jports/org_apache_velocity__velocity.jar:share/jports/commons_collections__commons_collections.jar:share/jports/commons_lang__commons_lang.jar: -ynet FILTER_YAHOO_ANY --wait-for-jvm-init 10 -Dzookeeper.jmx.log4j.disable=true -Dconfigsources=tcp/localhost:19070 -XX:OnOutOfMemoryError="kill -9 %p" com.yahoo.jdisc.core.BootstrapDaemon file:/home/vespa/lib/jars/container-disc-with-dependencies.jar +1354022570.876 example.yahoo.com 18151/1 configserver Container.com.yahoo.container.handler.config.HandlersConfigurerDi info Installing bundles from the latest application +1354022570.884 example.yahoo.com 18151/1 configserver Container.com.yahoo.container.handler.BundleLoader info Installing bundle from disk with reference 'file:/home/vespa/lib/jars/config-models/sherpa-config-model-plugin.jar' +1354022570.889 example.yahoo.com 18151/1 configserver Container.com.yahoo.container.handler.BundleLoader info Installing bundle from disk with reference 'file:/home/vespa/lib/jars/configserver-container-plugin.jar' +1354022574.285 example.yahoo.com 18151/1 configserver Container.com.yahoo.container.handler.config.HandlersConfigurerDi info Installing bundles from the latest application +1354022577.430 example.yahoo.com 18151/1 configserver Container.com.yahoo.statistics.Counter event count/1 name=requests value=0 +1354022577.431 example.yahoo.com 18151/1 configserver Container.com.yahoo.statistics.Counter event count/1 name=requestsCached value=0 +1354022577.432 example.yahoo.com 18151/1 configserver Container.com.yahoo.statistics.Counter event count/1 name=requestsNotCached value=0 +1354022577.432 example.yahoo.com 18151/1 configserver Container.com.yahoo.statistics.Counter event count/1 name=failedRequests value=0 +1354022577.432 example.yahoo.com 18151/1 configserver Container.com.yahoo.statistics.Counter event count/1 name=procTime value=0 +1354022577.432 example.yahoo.com 18151/1 configserver Container.com.yahoo.statistics.Counter event count/1 name=procTimeCached value=0 +1354022577.432 example.yahoo.com 18151/1 configserver Container.com.yahoo.statistics.Counter event count/1 name=procTimeNotCached value=0 +1354022577.489 example.yahoo.com 18151/1 configserver Container.com.yahoo.container.handler.config.HandlersConfigurerDi info Switched to the latest deployed set of handlers, and dependent components, e.g. search chains, searchers and document processors. Application switch number: 0 |