package org.jgroups.tests;

import org.jgroups.*;
import org.jgroups.protocols.TP;
import org.jgroups.tests.UnicastLoopbackTest.MyReceiver;
import org.jgroups.util.Promise;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * Tests unicasts to self (loopback of transport protocol)
 * @author Richard Achmatowicz 12 May 2008
 * @author Bela Ban Dec 31 2003
 * @version $Id: UnicastLoopbackTest.java,v 1.11 2008/05/12 15:53:49 rachmatowicz Exp $
 */
public class UnicastLoopbackTest extends ChannelTestBase {
    JChannel channel=null;


    @BeforeMethod
    protected void setUp() throws Exception {
        channel=createChannel();
    }

    @AfterMethod
    protected void tearDown() throws Exception {
        if(channel != null) {
            channel.close();
            channel=null;
        }
    }


    /**
     * Tests that when UNICAST messages are sent with TP.loopback == true, the following 
     * conditions hold:
     * (i) no messages touch the network
     * (ii) all messages are correctly received
     * 
     * @throws ChannelException
     * @throws ChannelClosedException
     * @throws ChannelNotConnectedException
     * @throws TimeoutException
     * @throws Exception
     */
    @Test
    public void testUnicastMsgsWithLoopback() throws ChannelException, ChannelClosedException, ChannelNotConnectedException, TimeoutException, Exception {

    	final long TIMEOUT = 2 * 1000 ;
    	final int NUM=1000;
    	long num_msgs_sent_before = 0 ;
    	long num_msgs_sent_after = 0 ;

    	Promise<Boolean> p = new Promise<Boolean>() ;
    	MyReceiver receiver = new MyReceiver(NUM, p) ;
    	channel.setReceiver(receiver) ;
    	channel.connect("demo-group") ;

    	Address local_addr=channel.getLocalAddress();

    	// set the loopback property on transport
    	setLoopbackProperty(channel, true) ;

    	num_msgs_sent_before = getNumMessagesSentViaNetwork(channel) ;

    	// send NUM UNICAST messages to ourself 
    	for(int i=1; i <= NUM; i++) {
    		channel.send(new Message(local_addr, null, new Integer(i)));
//  		try {
//  		Thread.sleep(1);
//  		}
//  		catch(InterruptedException e) {
//  		e.printStackTrace();
//  		}
    		if(i % 100 == 0)
    			System.out.println("-- sent " + i);
    	}

    	num_msgs_sent_after = getNumMessagesSentViaNetwork(channel) ;

    	// when loopback == true, messages should not touch the network
    	assertEquals(num_msgs_sent_before, num_msgs_sent_after, "Messages are (incorrectly) being sent via network") ;

    	try { 
    		// wait for all messages to be received
    		Boolean result = p.getResultWithTimeout(TIMEOUT) ;
    	}
    	catch(TimeoutException te) {
    		// timeout exception occurred 
    		fail("Test timed out before all messages were received") ;
    	}

    }


    /**
     * Returns the number of messages sent across the network.
     * 
     * @param ch 
     * @return the number of messages sent across the network
     * @throws Exception
     */
    private long getNumMessagesSentViaNetwork(JChannel ch) throws Exception {

    	TP transport = (TP) ch.getProtocolStack().getTransport();
    	if (transport == null) {
    		throw new Exception("transport layer is not present - check default stack configuration") ;
    	}

    	return transport.getNumMessagesSent();
    }


    /**
     * Set the value of the loopback property on the transport layer.
     * 
     * @param ch
     * @param loopback
     * @throws Exception
     */
    private void setLoopbackProperty(JChannel ch, boolean loopback) throws Exception {

    	TP transport =  (TP) ch.getProtocolStack().getTransport() ;
    	if (transport == null) {
    		throw new Exception("transport layer is not present - check default stack configuration") ;
    	}

    	// check if already set correctly
    	if ((loopback && transport.isLoopback()) || (!loopback && !transport.isLoopback()))
    		return ;

    	// otherwise, set it
    	transport.setLoopback(loopback);
    }

    /**
     * A receiver which waits for all messages to be received and 
     * then sets a promise.
     */
    private static class MyReceiver extends ReceiverAdapter {

    	private final int numExpected ;
    	private int numReceived;
    	private final Promise p ;

    	public MyReceiver(int numExpected, Promise p) {
    		this.numExpected = numExpected ;
    		this.numReceived = 0 ;
    		this.p = p ;
    	}

    	// when we receive a Message, we update the count of messages received
    	public void receive(Message msg) {

    		Integer num=(Integer)msg.getObject();
    		numReceived++;
    		if(num != null && num.intValue() % 100 == 0)
    			System.out.println("-- received " + num);

    		// if we have received NUM messages, set the result
    		if (numReceived >= numExpected)
    			p.setResult(Boolean.TRUE) ;
    	}

    	public int getNumMsgsReceived() {
    		return numReceived ;
    	}
    }


}

@@ -80,65 +80,8 @@
 TP;%0A
-import org.jgroups.tests.UnicastLoopbackTest.MyReceiver;%0A
 impo
@@ -444,17 +444,17 @@
 va,v 1.1
-1
+2
  2008/05
@@ -461,16 +461,16 @@
 /12 
-15:53:49
+22:03:36
  rac
@@ -2419,16 +2419,23 @@
 rk%0A    %09
+Assert.
 assertEq
@@ -2737,16 +2737,23 @@
  %0A    %09%09
+Assert.
 fail(%22Te
@@ -4271,16 +4271,25 @@
  Promise
+%3CBoolean%3E
  p ;%0A%0A  
@@ -4333,16 +4333,25 @@
  Promise
+%3CBoolean%3E
  p) %7B%0A  

Fix up some testNG related errors.