package org.jgroups.protocols;

import org.jgroups.Global;
import org.jgroups.JChannel;
import org.jgroups.protocols.pbcast.GMS;
import org.jgroups.protocols.pbcast.NAKACK2;
import org.jgroups.stack.Protocol;
import org.jgroups.util.Util;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

/**
 * Tests that unicast messages are not endlessly retransmitted when an ACK is dropped
 * (https://issues.jboss.org/browse/JGRP-1578)
 * @author Bela Ban
 * @since  3.3
 */
@Test(groups=Global.FUNCTIONAL,sequential=true)
public class UNICAST_DroppedAckTest {
    protected JChannel a, b;

    protected void setup(Class<? extends Protocol> unicast_class) throws Exception {
        a=createChannel(unicast_class, "A");
        b=createChannel(unicast_class, "B");
        a.connect("UNICAST_DroppedAckTest");
        b.connect("UNICAST_DroppedAckTest");
        Util.waitUntilAllChannelsHaveSameSize(10000, 1000, a, b);
    }

    @AfterMethod protected void destroy() {Util.close(b, a);}

    @DataProvider
    static Object[][] configProvider() {
        return new Object[][]{
          {UNICAST.class},
          {UNICAST3.class}
        };
    }

    @Test(dataProvider="configProvider")
    public void testNotEndlessXmits(Class<? extends Protocol> unicast_class) throws Exception {
        setup(unicast_class);

        DISCARD discard_a=(DISCARD)a.getProtocolStack().findProtocol(DISCARD.class);
        discard_a.setDropDownUnicasts(5); // drops the next 5 ACKs

        for(int i=1; i <= 5; i++)
            b.send(a.getAddress(), i);

        Protocol unicast_b=b.getProtocolStack().findProtocol(UNICAST.class, UNICAST3.class);
        for(int i=0; i < 10; i++) {
            int num_unacked_msgs=numUnackedMessages(unicast_b);
            System.out.println("num_unacked_msgs=" + num_unacked_msgs);
            if(num_unacked_msgs == 0)
                break;
            Util.sleep(1000);
        }

        assert numUnackedMessages(unicast_b) == 0 : "num_unacked_msgs on B should be 0 but is " + numUnackedMessages(unicast_b);
    }

    protected int numUnackedMessages(Protocol unicast) {
        if(unicast instanceof UNICAST)
            return ((UNICAST)unicast).getNumUnackedMessages();
        if(unicast instanceof UNICAST3)
            return ((UNICAST3)unicast).getNumUnackedMessages();
        throw new IllegalArgumentException("Protocol " + unicast.getClass().getSimpleName() + " needs to be UNICAST or UNICAST3");
    }


    protected JChannel createChannel(Class<? extends Protocol> unicast_class, String name) throws Exception {
        return new JChannel(new Protocol[] {
          new SHARED_LOOPBACK(),
          new PING().setValue("timeout", 300),
          new NAKACK2(),
          new DISCARD(),
          unicast_class.newInstance().setValue("xmit_interval", 500),
          new GMS()
        }).name(name);
    }
}
