
package org.jgroups.tests;


import junit.framework.TestCase;
import org.jgroups.JChannel;
import org.jgroups.ReceiverAdapter;
import org.jgroups.View;
import org.jgroups.util.Util;
import org.jgroups.stack.GossipRouter;


/**
 * Tests merging
 * @author Bela Ban
 * @version $Id: MergeTest.java,v 1.9.2.1 2009/05/11 17:43:14 rachmatowicz Exp $
 */
public class MergeTest extends TestCase {
    JChannel     channel;
    final int    TIMES=10;
    int    router_port = 0;
    String router_host = null;
    String props = null ;
    GossipRouter router;
    JChannel     ch1, ch2;
    private ViewChecker  checker;

    private String getTUNNELProps(String routerHost, int routerPort) {
        return
	    "TUNNEL(router_port=" + router_port + ";router_host=" + router_host + ";loopback=true):" +
            "PING(timeout=1000;num_initial_members=2;gossip_host=" + router_host +";gossip_port=" + router_port + "):" +
            "MERGE2(min_interval=3000;max_interval=5000):" +
            "FD(timeout=1000;max_tries=2;shun=false):" +
            "pbcast.NAKACK(gc_lag=50;retransmit_timeout=600,1200,2400,4800):" +
            "UNICAST(timeout=600,1200,2400):" +
            "pbcast.STABLE(desired_avg_gossip=20000):" +
            "pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;" +
            "print_local_addr=false;shun=false)";
    }


    public MergeTest(String name) {
        super(name);
    }

    protected void setUp() throws Exception {
        super.setUp();
        
    	router_host = System.getProperty("jgroups.tunnel.router_host", "127.0.0.1") ;
        router_port = Integer.parseInt(System.getProperty("jgroups.tunnel.router_port", "12001")) ;
    	
        startRouter(router_host, router_port);
        
        props = getTUNNELProps(router_host, router_port) ;
        
        checker=new ViewChecker();
        ch1=new JChannel(props);
        ch1.setReceiver(checker);
        ch1.connect("demo");
        ch2=new JChannel(props);
        ch2.setReceiver(checker);
        ch2.connect("demo");
        Util.sleep(1000);
    }

    public void tearDown() throws Exception {
        super.tearDown();
        ch2.close();
        ch1.close();
        stopRouter();
    }

    public void testPartitionAndSubsequentMerge() throws Exception {
        partitionAndMerge();
    }


    public void testTwoMerges() throws Exception {
        partitionAndMerge();
        partitionAndMerge();
    }



    private void partitionAndMerge() throws Exception {
        View v=ch2.getView();
        System.out.println("view is " + v);
        assertEquals("channel is supposed to have 2 members", 2, ch2.getView().size());

        System.out.println("++ simulating network partition by stopping the GossipRouter");
        stopRouter();

        System.out.println("sleeping for 10 secs");
        // Util.sleep(10000);
        checker.waitForNViews(2, 10000);

        v=ch1.getView();
        System.out.println("-- ch1.view: " + v);

        v=ch2.getView();
        System.out.println("-- ch2.view: " + v);
        assertEquals("view should be 1 (channels should have excluded each other", 1, v.size());

        System.out.println("++ simulating merge by starting the GossipRouter again");
        startRouter(router_host, router_port);

        System.out.println("sleeping for 30 secs");
        // Util.sleep(30000);
        checker.waitForNViews(2, 30000);

        v=ch1.getView();
        System.out.println("-- ch1.view: " + v);

        v=ch2.getView();
        System.out.println("-- ch2.view: " + v);

        assertEquals("channel is supposed to have 2 members again after merge", 2, ch2.getView().size());
    }


    private void startRouter(String router_host, int router_port) throws Exception {
        router=new GossipRouter(router_port, router_host);
        router.start();
    }

    private void stopRouter() {
        router.stop();
    }

    private static class ViewChecker extends ReceiverAdapter {
        final Object mutex=new Object();
        int          count=0;

        public void viewAccepted(View new_view) {
            synchronized(mutex) {
                count++;
                System.out.println("-- view: " + new_view);
                mutex.notifyAll();
            }
        }


        public void waitForNViews(int n, long timeout) {
            long sleep_time=timeout, curr, start;
            synchronized(mutex) {
                count=0;
                start=System.currentTimeMillis();
                while(count < n) {
                    try {mutex.wait(sleep_time);} catch(InterruptedException e) {}
                    curr=System.currentTimeMillis();
                    sleep_time-=(curr - start);
                    if(sleep_time <= 0)
                        break;
                }
            }
        }
    }


    public static void main(String[] args) {
        String[] testCaseName={MergeTest.class.getName()};
        junit.textui.TestRunner.main(testCaseName);
    }


}
