org.ros.internal.transport.MessageQueueIntegrationTest.java Source code

Java tutorial

Introduction

Here is the source code for org.ros.internal.transport.MessageQueueIntegrationTest.java

Source

/*
 * Copyright (C) 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.ros.internal.transport;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.HeapChannelBufferFactory;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.ros.concurrent.CancellableLoop;
import org.ros.concurrent.DefaultScheduledExecutorService;
import org.ros.internal.message.DefaultMessageDeserializer;
import org.ros.internal.message.DefaultMessageSerializer;
import org.ros.internal.message.Message;
import org.ros.internal.message.definition.MessageDefinitionReflectionProvider;
import org.ros.internal.message.topic.TopicMessageFactory;
import org.ros.internal.node.service.ServiceManager;
import org.ros.internal.node.topic.TopicParticipantManager;
import org.ros.internal.transport.queue.IncomingMessageQueue;
import org.ros.internal.transport.queue.OutgoingMessageQueue;
import org.ros.internal.transport.tcp.TcpRosClient;
import org.ros.internal.transport.tcp.TcpRosClientManager;
import org.ros.internal.transport.tcp.TcpRosServerPipelineFactory;
import org.ros.message.MessageDefinitionProvider;
import org.ros.message.MessageIdentifier;
import org.ros.message.MessageListener;

import io.smartspaces.testing.sizes.TestSizeLarge;

import java.net.InetSocketAddress;
import java.nio.ByteOrder;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author damonkohler@google.com (Damon Kohler)
 */
public class MessageQueueIntegrationTest {

    private static final boolean DEBUG = false;
    private static final Log log = LogFactory.getLog(MessageQueueIntegrationTest.class);

    private static final int QUEUE_CAPACITY = 128;

    private ExecutorService executorService;
    private TcpRosClientManager firstTcpClientManager;
    private TcpRosClientManager secondTcpClientManager;
    private OutgoingMessageQueue<Message> outgoingMessageQueue;
    private IncomingMessageQueue<std_msgs.String> firstIncomingMessageQueue;
    private IncomingMessageQueue<std_msgs.String> secondIncomingMessageQueue;
    private std_msgs.String expectedMessage;

    private class ServerHandler extends SimpleChannelHandler {
        @Override
        public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
            if (DEBUG) {
                log.info("Channel connected: " + e.getChannel().toString());
            }
            Channel channel = e.getChannel();
            outgoingMessageQueue.addChannel(channel);
            super.channelConnected(ctx, e);
        }

        @Override
        public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
            if (DEBUG) {
                log.info("Channel disconnected: " + e.getChannel().toString());
            }
            super.channelDisconnected(ctx, e);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
            if (DEBUG) {
                log.info("Channel exception: " + e.getChannel().toString());
            }
            e.getChannel().close();
            throw new RuntimeException(e.getCause());
        }
    }

    @Before
    public void setup() {
        executorService = new DefaultScheduledExecutorService();
        MessageDefinitionProvider messageDefinitionProvider = new MessageDefinitionReflectionProvider();
        TopicMessageFactory topicMessageFactory = new TopicMessageFactory(messageDefinitionProvider);
        expectedMessage = topicMessageFactory.newFromType(std_msgs.String._TYPE);
        expectedMessage.setData("Would you like to play a game?");
        outgoingMessageQueue = new OutgoingMessageQueue<Message>(new DefaultMessageSerializer(), executorService);
        firstIncomingMessageQueue = new IncomingMessageQueue<std_msgs.String>(
                new DefaultMessageDeserializer<std_msgs.String>(MessageIdentifier.of(std_msgs.String._TYPE),
                        topicMessageFactory),
                executorService);
        secondIncomingMessageQueue = new IncomingMessageQueue<std_msgs.String>(
                new DefaultMessageDeserializer<std_msgs.String>(MessageIdentifier.of(std_msgs.String._TYPE),
                        topicMessageFactory),
                executorService);
        firstTcpClientManager = new TcpRosClientManager(executorService);
        firstTcpClientManager.addNamedChannelHandler(firstIncomingMessageQueue.getMessageReceiver());
        secondTcpClientManager = new TcpRosClientManager(executorService);
        secondTcpClientManager.addNamedChannelHandler(secondIncomingMessageQueue.getMessageReceiver());
    }

    @After
    public void tearDown() {
        outgoingMessageQueue.shutdown();
        executorService.shutdown();
    }

    private void startRepeatingPublisher() {
        executorService.execute(new CancellableLoop() {
            @Override
            protected void loop() throws InterruptedException {
                outgoingMessageQueue.add(expectedMessage);
                Thread.sleep(100);
            }
        });
    }

    private Channel buildServerChannel() {
        TopicParticipantManager topicParticipantManager = new TopicParticipantManager();
        ServiceManager serviceManager = new ServiceManager();
        NioServerSocketChannelFactory channelFactory = new NioServerSocketChannelFactory(executorService,
                executorService);
        ServerBootstrap bootstrap = new ServerBootstrap(channelFactory);
        bootstrap.setOption("child.bufferFactory", new HeapChannelBufferFactory(ByteOrder.LITTLE_ENDIAN));
        bootstrap.setOption("child.keepAlive", true);
        ChannelGroup serverChannelGroup = new DefaultChannelGroup();
        TcpRosServerPipelineFactory serverPipelineFactory = new TcpRosServerPipelineFactory(serverChannelGroup,
                topicParticipantManager, serviceManager) {
            @Override
            public ChannelPipeline getPipeline() {
                ChannelPipeline pipeline = super.getPipeline();
                // We're not interested firstIncomingMessageQueue testing the
                // handshake here. Removing it means connections are established
                // immediately.
                pipeline.remove(TcpRosServerPipelineFactory.HANDSHAKE_HANDLER);
                pipeline.addLast("ServerHandler", new ServerHandler());
                return pipeline;
            }
        };
        bootstrap.setPipelineFactory(serverPipelineFactory);
        Channel serverChannel = bootstrap.bind(new InetSocketAddress(0));
        return serverChannel;
    }

    private TcpRosClient connect(TcpRosClientManager TcpClientManager, Channel serverChannel) {
        return TcpClientManager.connect("Foo", serverChannel.getLocalAddress());
    }

    private CountDownLatch expectMessage(IncomingMessageQueue<std_msgs.String> incomingMessageQueue)
            throws InterruptedException {
        final CountDownLatch latch = new CountDownLatch(1);
        incomingMessageQueue.addListener(new MessageListener<std_msgs.String>() {
            @Override
            public void onNewMessage(std_msgs.String message) {
                assertEquals(message, expectedMessage);
                latch.countDown();
            }
        }, QUEUE_CAPACITY);
        return latch;
    }

    private void expectMessages() throws InterruptedException {
        CountDownLatch firstLatch = expectMessage(firstIncomingMessageQueue);
        CountDownLatch secondLatch = expectMessage(secondIncomingMessageQueue);
        assertTrue(firstLatch.await(3, TimeUnit.SECONDS));
        assertTrue(secondLatch.await(3, TimeUnit.SECONDS));
    }

    @Category(TestSizeLarge.class)
    @Test
    public void testSendAndReceiveMessage() throws InterruptedException {
        startRepeatingPublisher();
        Channel serverChannel = buildServerChannel();
        connect(firstTcpClientManager, serverChannel);
        connect(secondTcpClientManager, serverChannel);
        expectMessages();
    }

    @Category(TestSizeLarge.class)
    @Test
    public void testSendAndReceiveLatchedMessage() throws InterruptedException {
        // Setting latched mode and writing a message should cause any
        // IncomingMessageQueues that connect in the future to receive the message.
        outgoingMessageQueue.setLatchMode(true);
        outgoingMessageQueue.add(expectedMessage);
        Channel serverChannel = buildServerChannel();
        firstIncomingMessageQueue.setLatchMode(true);
        secondIncomingMessageQueue.setLatchMode(true);
        connect(firstTcpClientManager, serverChannel);
        connect(secondTcpClientManager, serverChannel);
        // The first set of incoming messages could either be from the
        // OutgoingMessageQueue latching or the Subscriber latching. This is
        // equivalent to waiting for the message to arrive and ensures that we've
        // latched it in.
        expectMessages();
        // The second set of incoming messages can only be from the
        // IncomingMessageQueue latching since we only sent one message.
        expectMessages();
    }

    @Category(TestSizeLarge.class)
    @Test
    public void testSendAfterIncomingQueueShutdown() throws InterruptedException {
        startRepeatingPublisher();
        Channel serverChannel = buildServerChannel();
        connect(firstTcpClientManager, serverChannel);
        firstTcpClientManager.shutdown();
        outgoingMessageQueue.add(expectedMessage);
    }

    @Category(TestSizeLarge.class)
    @Test
    public void testSendAfterServerChannelClosed() throws InterruptedException {
        startRepeatingPublisher();
        Channel serverChannel = buildServerChannel();
        connect(firstTcpClientManager, serverChannel);
        assertTrue(serverChannel.close().await(1, TimeUnit.SECONDS));
        outgoingMessageQueue.add(expectedMessage);
    }

    @Category(TestSizeLarge.class)
    @Test
    public void testSendAfterOutgoingQueueShutdown() throws InterruptedException {
        startRepeatingPublisher();
        Channel serverChannel = buildServerChannel();
        connect(firstTcpClientManager, serverChannel);
        outgoingMessageQueue.shutdown();
        outgoingMessageQueue.add(expectedMessage);
    }
}