package se.chalmers.gdcn.tests;
import net.tomp2p.peers.Number160;
import net.tomp2p.storage.Data;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import se.chalmers.gdcn.control.WorkerReputationManager;
import se.chalmers.gdcn.files.TaskMeta;
import se.chalmers.gdcn.network.WorkerID;
import se.chalmers.gdcn.replica.ReplicaBox;
import se.chalmers.gdcn.replica.ReplicaManager;
import se.chalmers.gdcn.replica.ReplicaManager.ReplicaID;
import se.chalmers.gdcn.replica.ReplicaManagerBuilder;
import se.chalmers.gdcn.utils.Time;
import utils.TaskHolder;
import utils.TestUtils;
import utils.WorkerHolder;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.Semaphore;
/**
* Created by Leif on 2014-04-01.
*
*/
public class ReplicaManagerTest {
private ReplicaManagerBuilder builder;
private ReplicaManager replicaManager;
private WorkerReputationManager workerReputationManager;
private TaskMeta taskMetaA;
private TaskMeta taskMetaB;
private WorkerID workerA;
private WorkerID workerB;
private WorkerID workerC;
private WorkerID workerD;
private WorkerID myWorkerID;
@BeforeClass
public void setupClass() {
taskMetaA = TaskHolder.getTaskA();
taskMetaB = TaskHolder.getTaskB();
workerA = WorkerHolder.getWorkerA();
workerB = WorkerHolder.getWorkerB();
workerC = WorkerHolder.getWorkerC();
workerD = WorkerHolder.generate();
myWorkerID = WorkerHolder.getMyWorkerID();
}
@BeforeMethod
public void setupMethod(){
workerReputationManager = new WorkerReputationManager(myWorkerID);
workerReputationManager.registerWorker(workerA);
workerReputationManager.registerWorker(workerB);
workerReputationManager.registerWorker(workerC);
builder = new ReplicaManagerBuilder(workerReputationManager);
builder.setReplicas(2);
replicaManager = builder.create();
replicaManager.setWorkSelfIfRequired(false);
}
@Test
public void dataTest(){
assert taskMetaA.getTaskName().equals("PrimeTask_01");
assert taskMetaB.getTaskName().equals("PrimeTask_02");
}
@Test
public void keyTest(){
loadMeta(taskMetaA);
ReplicaBox replicaBoxA = replicaManager.giveReplicaToWorker(workerA);
ReplicaBox replicaBoxB = replicaManager.giveReplicaToWorker(workerB);
assert ! replicaBoxA.getResultKey().equals(replicaBoxB.getResultKey());
assert replicaBoxA.getResultKey().equals(replicaManager.getReplicaResultKey(replicaBoxA.getReplicaID()));
assert replicaBoxB.getResultKey().equals(replicaManager.getReplicaResultKey(replicaBoxB.getReplicaID()));
}
@Test
public void giveWorkTest(){
assert null == replicaManager.giveReplicaToWorker(workerA);
loadMeta(taskMetaA);
assert null != replicaManager.giveReplicaToWorker(workerA);
//Already has one from this task
assert null == replicaManager.giveReplicaToWorker(workerA);
assert null != replicaManager.giveReplicaToWorker(workerB);
//OBS not anymore since reputation...: No replicas left, only use 2 replicas in this test
// assert null == replicaManager.giveReplicaToWorker(workerC);
}
@Test
public void multiTest(){
loadMeta(taskMetaA);
loadMeta(taskMetaB);
ReplicaBox replicaBox1 = replicaManager.giveReplicaToWorker(workerA);
ReplicaBox replicaBox2 = replicaManager.giveReplicaToWorker(workerA);
assert replicaBox1 != null;
assert replicaBox2 != null;
assert ! replicaBox1.getReplicaID().equals(replicaBox2.getReplicaID());
//There are only two different tasks
assert null == replicaManager.giveReplicaToWorker(workerA);
}
@Test
public void finishReplicaTest(){
boolean exceptionThrown = false;
try{
replicaManager.replicaFinished(new ReplicaID("NotARealID"), new byte[1]);
} catch (Exception e){
exceptionThrown = true;
}
assert exceptionThrown;
loadMeta(taskMetaA);
ReplicaBox replicaBox = replicaManager.giveReplicaToWorker(workerA);
boolean exceptionThrown2 = false;
try{
replicaManager.replicaFinished(replicaBox.getReplicaID(), null);
} catch (Exception e){
exceptionThrown2 = true;
}
assert exceptionThrown2;
replicaManager.replicaFinished(replicaBox.getReplicaID(), new byte[1]);
}
@Test
public void serializeManagerTest() throws IOException, ClassNotFoundException {
loadMeta(taskMetaA);
final ReplicaBox replicaBox = replicaManager.giveReplicaToWorker(workerA);
Data serialized = new Data(replicaManager);
ReplicaManager replicaManagerCopy = (ReplicaManager) serialized.getObject();
Number160 originalKey = replicaBox.getResultKey();
Number160 firstKey = replicaManager.getReplicaResultKey(replicaBox.getReplicaID());
Number160 secondKey = replicaManagerCopy.getReplicaResultKey(replicaBox.getReplicaID());
assert originalKey.equals(firstKey);
assert originalKey.equals(secondKey);
//Throws exception if doesn't remember workerA:
replicaManagerCopy.replicaFinished(replicaBox.getReplicaID(), new byte[0]);
assert null == replicaManagerCopy.giveReplicaToWorker(workerA);
assert null != replicaManagerCopy.giveReplicaToWorker(workerB);
//It is truly a deep copy, not just a shallow copy
assert null != replicaManager.giveReplicaToWorker(workerB);
}
@Test
public void serializeReplicaBoxTest() throws IOException, ClassNotFoundException {
loadMeta(taskMetaA);
final ReplicaBox replicaBox = replicaManager.giveReplicaToWorker(workerA);
Data serialized = new Data(replicaBox);
ReplicaBox boxCopy = (ReplicaBox) serialized.getObject();
assert replicaBox.equals(boxCopy);
assert replicaBox != boxCopy;
}
@Test
public void isAssignedTest(){
loadMeta(taskMetaA);
ReplicaID falseReplicaID = new ReplicaID("SomeID");
assert ! replicaManager.isWorkerAssignedReplica(workerA, falseReplicaID);
ReplicaBox replicaBox = replicaManager.giveReplicaToWorker(workerA);
assert replicaManager.isWorkerAssignedReplica(workerA, replicaBox.getReplicaID());
assert ! replicaManager.isWorkerAssignedReplica(workerB, replicaBox.getReplicaID());
assert ! replicaManager.isWorkerAssignedReplica(workerA, falseReplicaID);
}
@Test
public void pendingTest(){
loadMeta(taskMetaA);
ReplicaBox replicaBoxA = replicaManager.giveReplicaToWorker(workerA);
ReplicaBox replicaBoxB = replicaManager.giveReplicaToWorker(workerB);
Set<ReplicaID> pending = replicaManager.pendingReplicaIDs();
assert pending.size() == 2;
assert pending.contains(replicaBoxA.getReplicaID());
assert pending.contains(replicaBoxB.getReplicaID());
}
@Test
public void outdateTimerTest(){
builder.setTimeoutLength(1, Time.MILLISECOND);
builder.setTimerUpdateInterval(15, Time.MILLISECOND);
replicaManager = builder.create();
replicaManager.setWorkSelfIfRequired(false);
loadMeta(taskMetaA);
ReplicaBox replicaBoxA = replicaManager.giveReplicaToWorker(workerA);
TestUtils.nap(25);
assert replicaManager.pendingReplicaIDs().size() == 0;
}
@Test
public void replicaFinishTest(){
builder.setTimeoutLength(1, Time.MILLISECOND);
replicaManager = builder.create();
replicaManager.setWorkSelfIfRequired(false);
loadMeta(taskMetaA);
ReplicaBox replicaBoxA = replicaManager.giveReplicaToWorker(workerA);
assert replicaManager.pendingReplicaIDs().contains(replicaBoxA.getReplicaID());
replicaManager.replicaFinished(replicaBoxA.getReplicaID(), new byte[1]);
assert replicaManager.pendingReplicaIDs().size() == 0;
}
@Test
public void outdateAfterFinishTest(){
loadMeta(taskMetaA);
ReplicaBox replicaBoxA = replicaManager.giveReplicaToWorker(workerA);
replicaManager.replicaFinished(replicaBoxA.getReplicaID(), new byte[1]);
boolean exceptionThrown = false;
try {
replicaManager.replicaOutdated(replicaBoxA.getReplicaID());
} catch (Exception e) {
exceptionThrown = true;
}
assert exceptionThrown;
}
@Test
public void replicaOutdatedTest(){
loadMeta(taskMetaA);
ReplicaBox replicaBoxA = replicaManager.giveReplicaToWorker(workerA);
ReplicaBox replicaBoxB = replicaManager.giveReplicaToWorker(workerB);
ReplicaBox replicaBoxC = replicaManager.giveReplicaToWorker(workerC);
replicaManager.replicaOutdated(replicaBoxA.getReplicaID());
assert replicaBoxC != null;
String taskA = replicaBoxA.getTaskMeta().getTaskName();
String taskB = replicaBoxB.getTaskMeta().getTaskName();
String taskC = replicaBoxC.getTaskMeta().getTaskName();
assert taskC.equals(taskA);
assert taskC.equals(taskB);
assert ! replicaBoxC.getReplicaID().equals(replicaBoxA.getReplicaID());
//No exception:
replicaManager.replicaFinished(replicaBoxA.getReplicaID(), new byte[1]);
replicaManager.replicaFinished(replicaBoxB.getReplicaID(), new byte[1]);
replicaManager.replicaFinished(replicaBoxC.getReplicaID(), new byte[1]);
assert replicaManager.pendingReplicaIDs().size() == 0;
}
@Test
public void serializedTimerTestOnReplicaManager() throws IOException, ClassNotFoundException {
builder.setTimeoutLength(150, Time.MILLISECOND);
builder.setTimerUpdateInterval(30, Time.MILLISECOND);
replicaManager = builder.create();
replicaManager.setWorkSelfIfRequired(false);
loadMeta(taskMetaA);
ReplicaBox replicaBoxA = replicaManager.giveReplicaToWorker(workerA);
Data serialized = new Data(replicaManager);
ReplicaManager deserialized = (ReplicaManager) serialized.getObject();
deserialized.setWorkSelfIfRequired(false);
assert deserialized.isWorkerAssignedReplica(workerA, replicaBoxA.getReplicaID());
assert deserialized.pendingReplicaIDs().contains(replicaBoxA.getReplicaID());
deserialized.resumeTimer();
TestUtils.nap(180);
assert deserialized.pendingReplicaIDs().size() == 0;
}
@Test
public void firstChoiceTest(){
loadMeta(taskMetaA);
loadMeta(taskMetaB);
ReplicaBox replicaBoxA = replicaManager.giveReplicaToWorker(workerA);
ReplicaBox replicaBoxB = replicaManager.giveReplicaToWorker(workerB);
//Should indeed work on different tasks since more reputation is required.
assert ! replicaBoxA.getTaskMeta().getTaskName().equals(replicaBoxB.getTaskMeta().getTaskName());
}
@Test
public void obviousChoiceTest(){
builder.setExpectedReputation(4);
replicaManager = builder.create();
promote(workerA, 4);
loadMeta(taskMetaA);
loadMeta(taskMetaB);
ReplicaBox replicaBoxA = replicaManager.giveReplicaToWorker(workerA);
ReplicaBox replicaBoxB = replicaManager.giveReplicaToWorker(workerB);
//Since workerA has high reputation, should work on same task as B which has no reputation
assert replicaBoxA.getTaskMeta().getTaskName().equals(replicaBoxB.getTaskMeta().getTaskName());
}
@Test
public void smartChoiceTest(){
builder.setExpectedReputation(4);
builder.setReplicas(2);
replicaManager = builder.create();
promote(workerA, 4);
//Reputation 4 is the limit in this case since floor() is called before ceiling().
loadMeta(taskMetaA);
loadMeta(taskMetaB);
ReplicaBox replicaBoxB = replicaManager.giveReplicaToWorker(workerB);
//Here A:4/1, B:4/2
ReplicaBox replicaBoxA = replicaManager.giveReplicaToWorker(workerA);
//Since workerA has high reputation, should work on same task as B which has no reputation
assert replicaBoxA.getTaskMeta().getTaskName().equals(replicaBoxB.getTaskMeta().getTaskName());
}
@Test
public void excessReplicaTest(){
builder.setExpectedReputation(0);
builder.setReplicas(1);
replicaManager = builder.create();
final Semaphore counter = new Semaphore(0);
replicaManager.setValidationListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
counter.release();
}
});
loadMeta(taskMetaA);
byte[] result = new byte[0];
ReplicaBox replicaBoxA = replicaManager.giveReplicaToWorker(workerA);
ReplicaBox replicaBoxB = replicaManager.giveReplicaToWorker(workerB);
replicaManager.replicaFinished(replicaBoxA.getReplicaID(), result);
//Do not validate yet, wait for B
assert counter.availablePermits() == 0;
replicaManager.replicaFinished(replicaBoxB.getReplicaID(), result);
//Validate here
assert counter.availablePermits() == 1;
assert null == replicaManager.giveReplicaToWorker(workerC);
//excess replica not allowed after Validation!
}
@Test
public void removeReplicaTest(){
builder.setExpectedReputation(0);
builder.setReplicas(1);
replicaManager = builder.create();
loadMeta(taskMetaA);
replicaManager.setValidationListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
//ignore
}
});
ReplicaBox replicaBox = replicaManager.giveReplicaToWorker(workerA);
replicaManager.replicaFinished(replicaBox.getReplicaID(), new byte[0]);
//Validates here
assert null == replicaManager.giveReplicaToWorker(workerB);
}
@Test
public void latecomerTest(){
builder.setExpectedReputation(0);
builder.setReplicas(1);
replicaManager = builder.create();
final Semaphore validCounter = new Semaphore(0);
final Semaphore lateCounter = new Semaphore(0);
replicaManager.setValidationListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println("Arrived "+evt.getPropertyName());
if("Validate".equals(evt.getPropertyName())){
validCounter.release();
} else if("Late".equals(evt.getPropertyName())){
lateCounter.release();
} else {
throw new AssertionError("Unknown: "+evt.getPropertyName());
}
}
});
loadMeta(taskMetaA);
byte[] result = new byte[0];
ReplicaBox replicaBoxA = replicaManager.giveReplicaToWorker(workerA);
ReplicaBox replicaBoxB = replicaManager.giveReplicaToWorker(workerB);
replicaManager.replicaFinished(replicaBoxA.getReplicaID(), result);
replicaManager.replicaOutdated(replicaBoxB.getReplicaID());
//Validate here
assert validCounter.availablePermits() == 1;
//Late comer
replicaManager.replicaFinished(replicaBoxB.getReplicaID(), result);
assert lateCounter.availablePermits() == 1;
}
@Test
public void advancedLatecomerTest(){
builder.setExpectedReputation(0);
builder.setReplicas(1);
replicaManager = builder.create();
final Semaphore validCounter = new Semaphore(0);
final Semaphore lateCounter = new Semaphore(0);
replicaManager.setValidationListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println("Arrived "+evt.getPropertyName());
if("Validate".equals(evt.getPropertyName())){
validCounter.release();
} else if("Late".equals(evt.getPropertyName())){
lateCounter.release();
} else {
throw new AssertionError("Unknown: "+evt.getPropertyName());
}
}
});
loadMeta(taskMetaA);
byte[] result = new byte[0];
ReplicaBox replicaBoxA = replicaManager.giveReplicaToWorker(workerA);
ReplicaBox replicaBoxB = replicaManager.giveReplicaToWorker(workerB);
replicaManager.replicaFinished(replicaBoxA.getReplicaID(), result);
//Can validate but chooses to wait for B
assert validCounter.availablePermits() == 0;
//Excess workers arrive
ReplicaBox replicaBoxC = replicaManager.giveReplicaToWorker(workerC);
ReplicaBox replicaBoxD = replicaManager.giveReplicaToWorker(workerD);
replicaManager.replicaFinished(replicaBoxB.getReplicaID(), result);
//Validate now, do not wait for excess workers!
assert validCounter.availablePermits() == 1;
//First late comer
replicaManager.replicaFinished(replicaBoxC.getReplicaID(), result);
assert lateCounter.availablePermits() == 1;
//Second late comer
replicaManager.replicaFinished(replicaBoxD.getReplicaID(), result);
assert lateCounter.availablePermits() == 2;
}
private void promote(WorkerID workerID, int times){
for(int i=0; i<times; ++i){
workerReputationManager.promoteWorker(workerID);
}
}
private void loadMeta(TaskMeta taskMeta){
TestUtils.loadMeta(taskMeta, this.replicaManager);
}
}