From 1f219378fa795c9a003a0b5a09dcd352463d13e8 Mon Sep 17 00:00:00 2001 From: Andrej Mak Date: Fri, 21 Oct 2016 14:41:09 +0200 Subject: [PATCH] Add netconf-netty-util tests Change-Id: I34adb433d6e078f3c95fd4a1516d44ce8ee0463a Signed-off-by: Andrej Mak --- .../handler/ThreadLocalTransformers.java | 35 ++-- .../AbstractNetconfSessionNegotiatorTest.java | 191 ++++++++++++++++++ .../nettyutil/AbstractNetconfSessionTest.java | 36 +--- .../nettyutil/TestingNetconfSession.java | 35 ++++ .../handler/ThreadLocalTransformersTest.java | 51 +++++ 5 files changed, 302 insertions(+), 46 deletions(-) create mode 100644 netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/AbstractNetconfSessionNegotiatorTest.java create mode 100644 netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/TestingNetconfSession.java create mode 100644 netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/handler/ThreadLocalTransformersTest.java diff --git a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ThreadLocalTransformers.java b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ThreadLocalTransformers.java index b8161f0bc1..35146e1f10 100644 --- a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ThreadLocalTransformers.java +++ b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ThreadLocalTransformers.java @@ -22,39 +22,28 @@ final class ThreadLocalTransformers { private static final ThreadLocal DEFAULT_TRANSFORMER = new ThreadLocal() { @Override protected Transformer initialValue() { - try { - return FACTORY.newTransformer(); - } catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) { - throw new IllegalStateException("Unexpected error while instantiating a Transformer", e); - } - }; + return createTransformer(); + } @Override public void set(final Transformer value) { throw new UnsupportedOperationException(); - }; + } }; private static final ThreadLocal PRETTY_TRANSFORMER = new ThreadLocal() { @Override protected Transformer initialValue() { - final Transformer ret; - - try { - ret = FACTORY.newTransformer(); - } catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) { - throw new IllegalStateException("Unexpected error while instantiating a Transformer", e); - } - + final Transformer ret = createTransformer(); ret.setOutputProperty(OutputKeys.INDENT, "yes"); ret.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); return ret; - }; + } @Override public void set(final Transformer value) { throw new UnsupportedOperationException(); - }; + } }; private ThreadLocalTransformers() { @@ -66,7 +55,7 @@ final class ThreadLocalTransformers { * * @return A transformer with default configuration based on the default implementation. */ - public static Transformer getDefaultTransformer() { + static Transformer getDefaultTransformer() { return DEFAULT_TRANSFORMER.get(); } @@ -76,7 +65,15 @@ final class ThreadLocalTransformers { * * @return A transformer with human-friendly configuration. */ - public static Transformer getPrettyTransformer() { + static Transformer getPrettyTransformer() { return PRETTY_TRANSFORMER.get(); } + + private static Transformer createTransformer() { + try { + return FACTORY.newTransformer(); + } catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) { + throw new IllegalStateException("Unexpected error while instantiating a Transformer", e); + } + } } diff --git a/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/AbstractNetconfSessionNegotiatorTest.java b/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/AbstractNetconfSessionNegotiatorTest.java new file mode 100644 index 0000000000..4da5b2c890 --- /dev/null +++ b/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/AbstractNetconfSessionNegotiatorTest.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.nettyutil; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.opendaylight.netconf.nettyutil.AbstractChannelInitializer.NETCONF_MESSAGE_AGGREGATOR; +import static org.opendaylight.netconf.nettyutil.AbstractChannelInitializer.NETCONF_MESSAGE_FRAME_ENCODER; + +import com.google.common.base.Optional; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelOutboundHandler; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.ssl.SslHandler; +import io.netty.util.HashedWheelTimer; +import io.netty.util.Timer; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.Promise; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.config.util.xml.XmlUtil; +import org.opendaylight.netconf.api.NetconfDocumentedException; +import org.opendaylight.netconf.api.NetconfSessionListener; +import org.opendaylight.netconf.api.NetconfSessionPreferences; +import org.opendaylight.netconf.api.messages.NetconfHelloMessage; +import org.opendaylight.netconf.api.xml.XmlNetconfConstants; +import org.opendaylight.netconf.nettyutil.handler.ChunkedFramingMechanismEncoder; +import org.opendaylight.netconf.nettyutil.handler.EOMFramingMechanismEncoder; +import org.opendaylight.netconf.nettyutil.handler.FramingMechanismHandlerFactory; +import org.opendaylight.netconf.nettyutil.handler.NetconfChunkAggregator; +import org.opendaylight.netconf.nettyutil.handler.NetconfEOMAggregator; +import org.opendaylight.netconf.nettyutil.handler.NetconfXMLToHelloMessageDecoder; +import org.opendaylight.netconf.util.messages.FramingMechanism; + +public class AbstractNetconfSessionNegotiatorTest { + + @Mock + private NetconfSessionListener listener; + @Mock + private Promise promise; + @Mock + private SslHandler sslHandler; + private EmbeddedChannel channel; + private AbstractNetconfSessionNegotiator negotiator; + private NetconfHelloMessage hello; + private NetconfHelloMessage helloBase11; + private NetconfXMLToHelloMessageDecoder xmlToHello; + private NetconfSessionPreferences prefs; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + channel = new EmbeddedChannel(); + xmlToHello = new NetconfXMLToHelloMessageDecoder(); + channel.pipeline().addLast(AbstractChannelInitializer.NETCONF_MESSAGE_ENCODER, new ChannelInboundHandlerAdapter()); + channel.pipeline().addLast(AbstractChannelInitializer.NETCONF_MESSAGE_DECODER, xmlToHello); + channel.pipeline().addLast(NETCONF_MESSAGE_FRAME_ENCODER, FramingMechanismHandlerFactory.createHandler(FramingMechanism.EOM)); + channel.pipeline().addLast(NETCONF_MESSAGE_AGGREGATOR, new NetconfEOMAggregator()); + hello = NetconfHelloMessage.createClientHello(Collections.emptySet(), Optional.absent()); + helloBase11 = NetconfHelloMessage.createClientHello(Collections.singleton(XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1), Optional.absent()); + prefs = new NetconfSessionPreferences(helloBase11); + doReturn(promise).when(promise).setFailure(any()); + doReturn(promise).when(promise).setSuccess(any()); + negotiator = new TestSessionNegotiator(prefs, promise, channel, new HashedWheelTimer(), listener, 100L); + } + + @Test + public void testStartNegotiation() throws Exception { + negotiator.startNegotiation(); + Assert.assertEquals(helloBase11, channel.readOutbound()); + } + + @Test + public void testStartNegotiationSsl() throws Exception { + doReturn(true).when(sslHandler).isSharable(); + doNothing().when(sslHandler).handlerAdded(any()); + doNothing().when(sslHandler).write(any(), any(), any()); + final Future handshakeFuture = channel.eventLoop().newSucceededFuture(channel); + doReturn(handshakeFuture).when(sslHandler).handshakeFuture(); + channel.pipeline().addLast(sslHandler); + negotiator.startNegotiation(); + verify(sslHandler, timeout(1000)).write(any(), eq(helloBase11), any()); + + } + + @Test + public void testStartNegotiationNotEstablished() throws Exception { + final ChannelOutboundHandler closedDetector = Mockito.spy(new ChannelOutboundHandlerAdapter()); + channel.pipeline().addLast("closedDetector", closedDetector); + doReturn(false).when(promise).isDone(); + doReturn(false).when(promise).isCancelled(); + negotiator.startNegotiation(); + verify(closedDetector, timeout(2000)).close(any(), any()); + } + + @Test + public void testGetSessionPreferences() throws Exception { + Assert.assertEquals(prefs, negotiator.getSessionPreferences()); + } + + @Test + public void testGetSessionForHelloMessage() throws Exception { + negotiator.startNegotiation(); + final AbstractNetconfSession session = negotiator.getSessionForHelloMessage(hello); + Assert.assertNotNull(session); + Assert.assertTrue(channel.pipeline().get(NETCONF_MESSAGE_AGGREGATOR) instanceof NetconfEOMAggregator); + Assert.assertTrue(channel.pipeline().get(NETCONF_MESSAGE_FRAME_ENCODER) instanceof EOMFramingMechanismEncoder); + } + + @Test + public void testGetSessionForHelloMessageBase11() throws Exception { + negotiator.startNegotiation(); + final AbstractNetconfSession session = negotiator.getSessionForHelloMessage(helloBase11); + Assert.assertNotNull(session); + Assert.assertTrue(channel.pipeline().get(NETCONF_MESSAGE_AGGREGATOR) instanceof NetconfChunkAggregator); + Assert.assertTrue(channel.pipeline().get(NETCONF_MESSAGE_FRAME_ENCODER) instanceof ChunkedFramingMechanismEncoder); + } + + @Test + public void testReplaceHelloMessageInboundHandler() throws Exception { + final List out = new ArrayList<>(); + final byte[] msg = "".getBytes(); + final ByteBuf msgBuf = Unpooled.wrappedBuffer(msg); + final ByteBuf helloBuf = Unpooled.wrappedBuffer(XmlUtil.toString(hello.getDocument()).getBytes()); + negotiator.startNegotiation(); + xmlToHello.decode(null, helloBuf, out); + xmlToHello.decode(null, msgBuf, out); + final AbstractNetconfSession session = mock(AbstractNetconfSession.class); + doNothing().when(session).handleMessage(any()); + negotiator.replaceHelloMessageInboundHandler(session); + verify(session, times(1)).handleMessage(any()); + } + + @Test + public void testNegotiationFail() throws Exception { + negotiator.startNegotiation(); + final RuntimeException cause = new RuntimeException("failure cause"); + channel.pipeline().fireExceptionCaught(cause); + verify(promise).setFailure(cause); + } + + private static class TestSessionNegotiator extends + AbstractNetconfSessionNegotiator> { + + + TestSessionNegotiator(final NetconfSessionPreferences sessionPreferences, + final Promise promise, final Channel channel, + final Timer timer, + final NetconfSessionListener sessionListener, + final long connectionTimeoutMillis) { + super(sessionPreferences, promise, channel, timer, sessionListener, connectionTimeoutMillis); + } + + @Override + protected TestingNetconfSession getSession(final NetconfSessionListener sessionListener, final Channel channel, + final NetconfHelloMessage message) throws NetconfDocumentedException { + return new TestingNetconfSession(sessionListener, channel, 0L); + } + + @Override + protected void handleMessage(final NetconfHelloMessage netconfHelloMessage) throws Exception { + + } + } + + +} \ No newline at end of file diff --git a/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/AbstractNetconfSessionTest.java b/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/AbstractNetconfSessionTest.java index 5c2bb755b1..8edfca9296 100644 --- a/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/AbstractNetconfSessionTest.java +++ b/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/AbstractNetconfSessionTest.java @@ -38,18 +38,17 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.opendaylight.netconf.api.NetconfMessage; -import org.opendaylight.netconf.api.NetconfSession; import org.opendaylight.netconf.api.NetconfSessionListener; import org.opendaylight.netconf.api.NetconfTerminationReason; -import org.opendaylight.netconf.nettyutil.handler.exi.NetconfStartExiMessage; import org.opendaylight.netconf.api.messages.NetconfHelloMessage; import org.opendaylight.netconf.api.messages.NetconfHelloMessageAdditionalHeader; +import org.opendaylight.netconf.nettyutil.handler.exi.NetconfStartExiMessage; import org.openexi.proc.common.EXIOptions; public class AbstractNetconfSessionTest { @Mock - private NetconfSessionListener listener; + private NetconfSessionListener listener; @Mock private Channel channel; @Mock @@ -64,10 +63,10 @@ public class AbstractNetconfSessionTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - doNothing().when(listener).onMessage(any(NetconfSession.class), any(NetconfMessage.class)); - doNothing().when(listener).onSessionUp(any(NetconfSession.class)); - doNothing().when(listener).onSessionDown(any(NetconfSession.class), any(Exception.class)); - doNothing().when(listener).onSessionTerminated(any(NetconfSession.class), any(NetconfTerminationReason.class)); + doNothing().when(listener).onMessage(any(TestingNetconfSession.class), any(NetconfMessage.class)); + doNothing().when(listener).onSessionUp(any(TestingNetconfSession.class)); + doNothing().when(listener).onSessionDown(any(TestingNetconfSession.class), any(Exception.class)); + doNothing().when(listener).onSessionTerminated(any(TestingNetconfSession.class), any(NetconfTerminationReason.class)); doReturn(writeFuture).when(writeFuture).addListener(any(GenericFutureListener.class)); @@ -81,7 +80,7 @@ public class AbstractNetconfSessionTest { doReturn(eventLoop).when(channel).eventLoop(); doAnswer(new Answer() { @Override - public Void answer(InvocationOnMock invocation) throws Throwable { + public Void answer(final InvocationOnMock invocation) throws Throwable { final Object[] args = invocation.getArguments(); final Runnable runnable = (Runnable) args[0]; runnable.run(); @@ -113,7 +112,7 @@ public class AbstractNetconfSessionTest { testingNetconfSession.sessionUp(); testingNetconfSession.close(); verify(channel).close(); - verify(listener).onSessionTerminated(any(NetconfSession.class), any(NetconfTerminationReason.class)); + verify(listener).onSessionTerminated(any(TestingNetconfSession.class), any(NetconfTerminationReason.class)); } @Test @@ -149,7 +148,7 @@ public class AbstractNetconfSessionTest { verifyZeroInteractions(listener); testingNetconfSession.sessionUp(); testingNetconfSession.endOfInput(); - verify(listener).onSessionDown(any(NetconfSession.class), any(Exception.class)); + verify(listener).onSessionDown(any(TestingNetconfSession.class), any(Exception.class)); } @Test @@ -160,21 +159,4 @@ public class AbstractNetconfSessionTest { verify(channel).writeAndFlush(clientHello); } - private static class TestingNetconfSession extends AbstractNetconfSession> { - - protected TestingNetconfSession(final NetconfSessionListener sessionListener, final Channel channel, final long sessionId) { - super(sessionListener, channel, sessionId); - } - - @Override - protected NetconfSession thisInstance() { - return this; - } - - @Override - protected void addExiHandlers(final ByteToMessageDecoder decoder, final MessageToByteEncoder encoder) {} - - @Override - public void stopExiCommunication() {} - } } \ No newline at end of file diff --git a/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/TestingNetconfSession.java b/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/TestingNetconfSession.java new file mode 100644 index 0000000000..b7a1c6c741 --- /dev/null +++ b/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/TestingNetconfSession.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.nettyutil; + +import io.netty.channel.Channel; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.MessageToByteEncoder; +import org.opendaylight.netconf.api.NetconfMessage; +import org.opendaylight.netconf.api.NetconfSessionListener; + +class TestingNetconfSession extends AbstractNetconfSession> { + + TestingNetconfSession(final NetconfSessionListener sessionListener, final Channel channel, final long sessionId) { + super(sessionListener, channel, sessionId); + } + + @Override + protected TestingNetconfSession thisInstance() { + return this; + } + + @Override + protected void addExiHandlers(final ByteToMessageDecoder decoder, final MessageToByteEncoder encoder) { + } + + @Override + public void stopExiCommunication() { + } +} diff --git a/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/handler/ThreadLocalTransformersTest.java b/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/handler/ThreadLocalTransformersTest.java new file mode 100644 index 0000000000..456663b3b9 --- /dev/null +++ b/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/handler/ThreadLocalTransformersTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.nettyutil.handler; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import javax.xml.transform.Transformer; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class ThreadLocalTransformersTest { + + private ExecutorService executorService; + + @Before + public void setUp() throws Exception { + executorService = Executors.newSingleThreadExecutor(); + } + + @Test + public void testGetDefaultTransformer() throws Exception { + final Transformer t1 = ThreadLocalTransformers.getDefaultTransformer(); + final Transformer t2 = ThreadLocalTransformers.getDefaultTransformer(); + Assert.assertSame(t1, t2); + final Future future = executorService.submit(ThreadLocalTransformers::getDefaultTransformer); + Assert.assertNotSame(t1, future.get()); + } + + @Test + public void testGetPrettyTransformer() throws Exception { + final Transformer t1 = ThreadLocalTransformers.getPrettyTransformer(); + final Transformer t2 = ThreadLocalTransformers.getPrettyTransformer(); + Assert.assertSame(t1, t2); + final Future future = executorService.submit(ThreadLocalTransformers::getPrettyTransformer); + Assert.assertNotSame(t1, future.get()); + } + + @After + public void tearDown() throws Exception { + executorService.shutdown(); + } +} \ No newline at end of file -- 2.36.6