package org.jgroups.protocols;
import org.jgroups.Address;
import org.jgroups.Global;
import org.jgroups.util.Util;
import org.testng.annotations.Test;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Test battery to the behavior of the AckCollector structure in ACK_SEQUENCER
*
* @author Pedro Ruivo
* @since 3.1
*/
@Test(groups = Global.FUNCTIONAL, sequential = true)
public class ACK_SEQUENCER_AckCollectorTest {
public void testNonBlockingWaitDueToEmptyList() {
Timer timer = new Timer();
timer.schedule(cancelTask(), 10000);
ACK_SEQUENCER.AckCollector ackCollector = new ACK_SEQUENCER.AckCollector();
try {
ackCollector.deliver(10, Collections.<Address>emptyList(), Util.createRandomAddress());
} catch (InterruptedException e) {
assert false : "blocked when try to await(10, emptyList)";
}
timer.cancel();
}
public void testNonBlockingWaitDueToZeroAcksNeeded() {
List<Address> addressList = addressList();
Timer timer = new Timer();
timer.schedule(cancelTask(), 10000);
ACK_SEQUENCER.AckCollector ackCollector = new ACK_SEQUENCER.AckCollector();
try {
ackCollector.deliver(0, addressList, Util.createRandomAddress());
} catch (InterruptedException e) {
assert false : "blocked when try to await(0, nonEmptyList)";
}
timer.cancel();
}
public void testNonBlockingWaitDueToNegativeAcksNeeded() {
List<Address> addressList = addressList();
Timer timer = new Timer();
timer.schedule(cancelTask(), 10000);
ACK_SEQUENCER.AckCollector ackCollector = new ACK_SEQUENCER.AckCollector();
try {
ackCollector.deliver(-1, addressList, Util.createRandomAddress());
} catch (InterruptedException e) {
assert false : "blocked when try to await(-1, nonEmptyList)";
}
timer.cancel();
}
public void testSameNumberOfAcksAndMembers() {
List<Address> addressList = addressList();
ACK_SEQUENCER.AckCollector ackCollector = new ACK_SEQUENCER.AckCollector();
ackCollector.populateIfNeeded(addressList.size(), addressList);
check(ackCollector, addressList.size(), addressList.size());
CountDownLatch countDownLatch = new CountDownLatch(1);
Thread thread = createNewAwaitThread(ackCollector, countDownLatch);
thread.start();
for (Address address : addressList) {
checkBlocked(countDownLatch, ackCollector);
ackCollector.ack(address, 0, Collections.<Address>emptyList());
}
checkUnblocked(countDownLatch, ackCollector);
check(ackCollector, 0, 0);
}
public void testHigherNumberOfAcksNeededThanMemberList() {
List<Address> addressList = addressList();
ACK_SEQUENCER.AckCollector ackCollector = new ACK_SEQUENCER.AckCollector();
ackCollector.populateIfNeeded(50, addressList);
check(ackCollector, 50, addressList.size());
CountDownLatch countDownLatch = new CountDownLatch(1);
Thread thread = createNewAwaitThread(ackCollector, countDownLatch);
thread.start();
for (Address address : addressList) {
checkBlocked(countDownLatch, ackCollector);
ackCollector.ack(address, 0, Collections.<Address>emptyList());
}
checkUnblocked(countDownLatch, ackCollector);
check(ackCollector, 40, 0);
}
public void testLowerAcksNeededThanMemberList() {
List<Address> addressList = addressList();
ACK_SEQUENCER.AckCollector ackCollector = new ACK_SEQUENCER.AckCollector();
ackCollector.populateIfNeeded(2, addressList);
check(ackCollector, 2, addressList.size());
CountDownLatch countDownLatch = new CountDownLatch(1);
Thread thread = createNewAwaitThread(ackCollector, countDownLatch);
thread.start();
checkBlocked(countDownLatch, ackCollector);
ackCollector.ack(addressList.get(0), 0, Collections.<Address>emptyList());
checkBlocked(countDownLatch, ackCollector);
ackCollector.ack(addressList.get(1), 0, Collections.<Address>emptyList());
checkUnblocked(countDownLatch, ackCollector);
//its clear the member list after unblocking
check(ackCollector, 0, 0);
}
public void testNonMemberAck() {
List<Address> addressList = addressList();
ACK_SEQUENCER.AckCollector ackCollector = new ACK_SEQUENCER.AckCollector();
ackCollector.populateIfNeeded(addressList.size(), addressList);
check(ackCollector, addressList.size(), addressList.size());
ackCollector.ack(Util.createRandomAddress("B"), 0, Collections.<Address>emptyList());
check(ackCollector, addressList.size(), addressList.size());
}
public void testDuplicatedAcks() {
List<Address> addressList = addressList();
ACK_SEQUENCER.AckCollector ackCollector = new ACK_SEQUENCER.AckCollector();
ackCollector.populateIfNeeded(addressList.size(), addressList);
check(ackCollector, addressList.size(), addressList.size());
ackCollector.ack(addressList.get(0), 0, Collections.<Address>emptyList());
ackCollector.ack(addressList.get(0), 0, Collections.<Address>emptyList());
ackCollector.ack(addressList.get(0), 0, Collections.<Address>emptyList());
check(ackCollector, addressList.size() - 1, addressList.size() - 1);
}
public void testOneMemberAlive() {
Address coordinator = Util.createRandomAddress("A");
List<Address> addressList = Collections.<Address>singletonList(coordinator);
ACK_SEQUENCER.AckCollector ackCollector = new ACK_SEQUENCER.AckCollector();
ackCollector.populateIfNeeded(addressList.size(), addressList);
CountDownLatch countDownLatch = new CountDownLatch(1);
Thread thread = createNewAwaitThread(ackCollector, countDownLatch, coordinator);
thread.start();
checkUnblocked(countDownLatch, ackCollector);
}
private void check(ACK_SEQUENCER.AckCollector ackCollector, int expectedAcks, int expectedSize) {
assert expectedAcks == ackCollector.getNumberOfAcksMissing() : "Wrong state detected. Expected acks missing is " +
expectedAcks + " but the collector has " + ackCollector.getNumberOfAcksMissing();
assert expectedSize == ackCollector.getSizeOfMembersMissing() : "Wrong state detected. Expected member size is " +
expectedSize + " but the collector has " + ackCollector.getSizeOfMembersMissing();
}
private void checkBlocked(CountDownLatch countDownLatch, ACK_SEQUENCER.AckCollector ackCollector) {
assert countDownLatch.getCount() == 1 : "Thread was already unblocked but acks is still needed. State is " + ackCollector;
}
private void checkUnblocked(CountDownLatch countDownLatch, ACK_SEQUENCER.AckCollector ackCollector) {
try {
countDownLatch.await(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
assert false : "Thread is still blocked but *no* acks is needed. State is " + ackCollector;
}
}
private TimerTask cancelTask() {
final Thread workingThread = Thread.currentThread();
return new TimerTask() {
@Override
public void run() {
workingThread.interrupt();
}
};
}
private List<Address> addressList() {
List<Address> addressList = new LinkedList<Address>();
for (int i = 0; i < 10; i++) {
addressList.add(Util.createRandomAddress("A" + i));
}
return addressList;
}
private Thread createNewAwaitThread(final ACK_SEQUENCER.AckCollector ackCollector, final CountDownLatch countDownLatch) {
return new Thread() {
@Override
public void run() {
try {
ackCollector.deliver(0, Collections.<Address>emptyList(), Util.createRandomAddress());
countDownLatch.countDown();
} catch (InterruptedException e) {/*just ignore*/}
}
};
}
private Thread createNewAwaitThread(final ACK_SEQUENCER.AckCollector ackCollector, final CountDownLatch countDownLatch,
final Address coordinatorAddress) {
return new Thread() {
@Override
public void run() {
try {
ackCollector.deliver(0, Collections.<Address>emptyList(), coordinatorAddress);
countDownLatch.countDown();
} catch (InterruptedException e) {/*just ignore*/}
}
};
}
}