Use a random port in test server 03/5703/2
authorDavid K. Bainbridge <dbainbri@ciena.com>
Wed, 19 Mar 2014 18:04:51 +0000 (11:04 -0700)
committerRobert Varga <rovarga@cisco.com>
Tue, 25 Mar 2014 10:48:55 +0000 (11:48 +0100)
This patch updates the test server to bind to a random port number and expose
this port number via a Future can be fetched by the test client so that it
knows to which port to connect.

Version 2:
Modified the code to use the guava SettableFuture as opposed to a custom built
future, as well as cleaned up a few other things, including makding sure that
the message was received from the server before considering the test passed.
was seeing different behavior on different platforms without this (i.e. some
times the message was display, sometimes not). Also, failed the test case wh
exceptions were caught.

Change-Id: Ia11c374bfbf206a6214d2afe3ba328b1231d6fad
Signed-off-by: David K. Bainbridge <dbainbri@ciena.com>
Signed-off-by: Robert Varga <rovarga@cisco.com>
websocket/websocket-client/pom.xml
websocket/websocket-client/src/test/java/org/opendaylight/yangtools/websocket/WebSocketClientTest.java
websocket/websocket-client/src/test/java/org/opendaylight/yangtools/websocket/server/WebSocketServer.java

index a9309503601994f67c6eb4db970dbb5f59930eda..9a1c8df5ae31ecb16f12c60fc8d21114e0ab0341 100644 (file)
     <packaging>jar</packaging>
 
     <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
         <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-codec-http</artifactId>
index 9f94887f9a6e0b35740a1597155e8687404f2b4b..a5bbb87b491dc37af1bbf0dc87030281f48bcadd 100644 (file)
@@ -7,60 +7,95 @@
  */
 package org.opendaylight.yangtools.websocket;
 
-import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
 import java.net.URI;
 import java.net.URISyntaxException;
+
 import org.junit.Before;
 import org.junit.Test;
-import org.opendaylight.yangtools.websocket.client.WebSocketIClient;
-import org.opendaylight.yangtools.websocket.server.WebSocketServer;
+import org.junit.Assert;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.opendaylight.yangtools.websocket.client.WebSocketIClient;
+import org.opendaylight.yangtools.websocket.server.WebSocketServer;
+
+import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
+
+import com.google.common.util.concurrent.SettableFuture;
+
 public class WebSocketClientTest {
     private static final Logger logger = LoggerFactory.getLogger(WebSocketClientTest.class.toString());
     private static final String MESSAGE = "Take me to your leader!";
-    private static final int port = 8080;
     private Thread webSocketServerThread;
 
+    /**
+     * Tracks if the message from the server has been received
+     */
+    private SettableFuture<Boolean> messageReceived = SettableFuture.<Boolean>create();
+
+    /**
+     * Tracks the port on which the server is listening
+     */
+    private int port = 0;
 
     @Before
     public void startWebSocketServer(){
         try {
-            WebSocketServer webSocketServer = new WebSocketServer(port);
+            WebSocketServer webSocketServer = new WebSocketServer(0);
             webSocketServerThread = new Thread(webSocketServer);
             webSocketServerThread.setDaemon(false);
             webSocketServerThread.start();
+            port = webSocketServer.getPort().get();
         } catch (Exception e) {
             logger.trace("Error starting websocket server");
         }
     }
+
     @Test
     public void connectAndSendData(){
 
         URI uri = null;
         try {
-            uri = new URI("ws://localhost:8080/websocket");
+            uri = new URI(String.format("ws://localhost:%d/websocket", port));
+            logger.info("CLIENT: " + uri);
             ClientMessageCallback messageCallback = new ClientMessageCallback();
             WebSocketIClient wsClient = new WebSocketIClient(uri,messageCallback);
             try {
                 wsClient.connect();
                 wsClient.writeAndFlush(MESSAGE);
                 wsClient.writeAndFlush(new CloseWebSocketFrame());
+
+                /*
+                 * Wait for up to 5 seconds for the message to be received. If
+                 * after that time, the message has not been received then
+                 * consider this a failed test.
+                 */
+                messageReceived.get(5, TimeUnit.SECONDS);
+
                 webSocketServerThread.interrupt();
             } catch (InterruptedException e) {
-                logger.info("WebSocket client couldn't connect to : "+uri);
+                logger.info("WebSocket client couldn't connect to : " + uri);
+                Assert.fail("WebSocker client could not connect to : " + uri);
+            } catch (ExecutionException | TimeoutException toe) {
+                logger.info("Message not received");
+                Assert.fail(toe.toString());
             }
         } catch (URISyntaxException e) {
             logger.info("There is an error in URL sytnax {}",e);
+            Assert.fail("There is an error in URL sytnax");
         }
     }
 
     private class ClientMessageCallback implements org.opendaylight.yangtools.websocket.client.callback.ClientMessageCallback {
         @Override
         public void onMessageReceived(Object message) {
-           logger.info("received message {}",message);
-           System.out.println("received message : " + message);
+            logger.info("received message {}",message);
+            messageReceived.set(true);
         }
     }
 }
index 08d51b3f6815972620b3e37470472d2355be9f99..8fb379cb79d30182842080c88a9e9b9661d75071 100644 (file)
@@ -20,9 +20,20 @@ import io.netty.channel.Channel;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioServerSocketChannel;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.net.SocketAddress;
+import java.net.InetSocketAddress;
+
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+import com.google.common.util.concurrent.SettableFuture;
+
 /**
  * A HTTP server which serves Web Socket requests at:
  *
@@ -44,19 +55,28 @@ import org.slf4j.LoggerFactory;
  */
 public class WebSocketServer implements Runnable {
 
-    private final int port;
+    /**
+     * Utilized to get the port on which the server listens. This is a future as
+     * the port is selected dynamically from available ports, thus until the
+     * server is started the value will not be established.
+     */
+    private final SettableFuture<Integer> port;
+
+    /**
+     * Maintains the port number with which the class was initialized.
+     */
+    private final int inPort;
     private final ServerBootstrap bootstrap = new ServerBootstrap();
     private final EventLoopGroup bossGroup = new NioEventLoopGroup();
     private final EventLoopGroup workerGroup = new NioEventLoopGroup();
     private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class.toString());
 
-    public WebSocketServer(int port) {
-        this.port = port;
+
+    public WebSocketServer(int inPort) {
+        this.inPort = inPort;
+        port = SettableFuture.<Integer>create();
     }
 
-    /**
-     * Tries to start web socket server. 
-     */
     public void run(){
         try {
             startServer();
@@ -64,27 +84,35 @@ public class WebSocketServer implements Runnable {
             logger.info("Exception occured while starting webSocket server {}",e);
         }
     }
-    
-    /**
-     * Start web socket server at {@link #port}.
-     * @throws Exception
-     */
+
+    public Future<Integer> getPort() {
+        return port;
+    }
+
     public void startServer() throws Exception {
         try {
             bootstrap.group(bossGroup, workerGroup)
              .channel(NioServerSocketChannel.class)
              .childHandler(new WebSocketServerInitializer());
 
-            Channel ch = bootstrap.bind(port).sync().channel();
-            logger.info("Web socket server started at port " + port + '.');
-            logger.info("Open your browser and navigate to http://localhost:" + port + '/');
+            Channel ch = bootstrap.bind(inPort).sync().channel();
+            SocketAddress localSocket = ch.localAddress();
+            try {
+                port.set(((InetSocketAddress) localSocket).getPort());
+            } catch (ClassCastException cce) {
+                throw new ExecutionException("Unknown socket address type", cce);
+            }
+            logger.info("Web socket server started at port " + port.get() + '.');
+            logger.info("Open your browser and navigate to http://localhost:" + port.get() + '/');
 
-            ch.closeFuture().sync();
+            try {
+                ch.closeFuture().sync();
+            } catch (InterruptedException ie) {
+                // No op, sometimes the server is shutdown hard
+            }
         } finally {
             bossGroup.shutdownGracefully();
             workerGroup.shutdownGracefully();
         }
     }
-
-
 }