package se.sics.gvod.ls.video;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.Semaphore;
import java.util.logging.Level;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.junit.Ignore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import se.sics.gvod.address.Address;
import se.sics.gvod.common.UtilityVod;
import se.sics.gvod.config.VodConfig;
import se.sics.gvod.ls.http.HTTPStreamingClient;
import se.sics.gvod.net.NatNetworkControl;
import se.sics.gvod.net.NettyInit;
import se.sics.gvod.net.NettyNetwork;
import se.sics.gvod.net.Transport;
import se.sics.gvod.net.VodAddress;
import se.sics.gvod.net.VodMsgFrameDecoder;
import se.sics.gvod.net.VodNetwork;
import se.sics.gvod.net.events.PortBindRequest;
import se.sics.gvod.net.events.PortBindResponse;
import se.sics.gvod.timer.Timer;
import se.sics.gvod.timer.UUID;
import se.sics.gvod.timer.java.JavaTimer;
import se.sics.gvod.video.msgs.EncodedSubPiece;
import se.sics.gvod.video.msgs.Piece;
import se.sics.gvod.video.msgs.SubPiece;
import se.sics.gvod.video.msgs.VideoPieceMsg;
import se.sics.kompics.*;
import se.sics.kompics.nat.utils.getip.ResolveIp;
import se.sics.kompics.nat.utils.getip.ResolveIpPort;
import se.sics.kompics.nat.utils.getip.events.GetIpRequest;
import se.sics.kompics.nat.utils.getip.events.GetIpResponse;
/**
* Unit test for simple App.
*/
public class EncodedSubPieceTest
extends TestCase {
private static final Logger logger = LoggerFactory.getLogger(EncodedSubPieceTest.class);
private boolean testStatus = true;
/**
* Create the test case
*
*/
public EncodedSubPieceTest(String testName) {
super(testName);
}
/**
* @return the suite of tests being tested
*/
public static Test suite() {
return new TestSuite(EncodedSubPieceTest.class);
}
public static void setTestObj(EncodedSubPieceTest testObj) {
EncodedSubPieceTest.TestStClientComponent.testObj = testObj;
}
public static class TestStClientComponent extends ComponentDefinition {
private Component client2;
private Component client;
private Component server;
private Component timer;
private Component resolveIp;
private static EncodedSubPieceTest testObj = null;
private VodAddress client2Addr;
private VodAddress clientAddr;
private VodAddress serverAddr;
private UtilityVod utility = new UtilityVod(10, 200, 15);
// EncodedSubPieceTest
Map<Integer, VideoFEC> fecs, decodeFecs, decodeFecs2;
int numberOfTestSubPieces, numberOfTestSubPiecesLeft, numberOfTestSubPiecesLeft2;
Random random;
public TestStClientComponent() {
timer = create(JavaTimer.class);
client2 = create(NettyNetwork.class);
client = create(NettyNetwork.class);
server = create(NettyNetwork.class);
resolveIp = create(ResolveIp.class);
connect(client.getNegative(Timer.class), timer.getPositive(Timer.class));
connect(client2.getNegative(Timer.class), timer.getPositive(Timer.class));
connect(server.getNegative(Timer.class), timer.getPositive(Timer.class));
subscribe(handleStart, control);
subscribe(handleMsgTimeout, timer.getPositive(Timer.class));
subscribe(handleConnectRequest, server.getPositive(VodNetwork.class));
subscribe(handleDataResponse, client.getPositive(VodNetwork.class));
subscribe(handleDataResponse, client2.getPositive(VodNetwork.class));
subscribe(handleGetIpResponse, resolveIp.getPositive(ResolveIpPort.class));
}
public Handler<Start> handleStart = new Handler<Start>() {
@Override
public void handle(Start event) {
System.out.println("Starting");
trigger(new GetIpRequest(false),
resolveIp.getPositive(ResolveIpPort.class));
}
};
public Handler<GetIpResponse> handleGetIpResponse = new Handler<GetIpResponse>() {
@Override
public void handle(GetIpResponse event) {
InetAddress ip = event.getBoundIp();
int client2Port = 59878;
int clientPort = 59877;
int serverPort = 33221;
try {
ip = InetAddress.getLocalHost();
} catch (UnknownHostException ex) {
logger.error("UnknownHostException");
testObj.fail();
}
Address cAddr = new Address(ip, clientPort, 0);
Address c2Addr = new Address(ip, client2Port, 2);
Address sAddr = new Address(ip, serverPort, 1);
clientAddr = new VodAddress(cAddr, VodConfig.SYSTEM_OVERLAY_ID);
client2Addr = new VodAddress(c2Addr, VodConfig.SYSTEM_OVERLAY_ID);
serverAddr = new VodAddress(sAddr, VodConfig.SYSTEM_OVERLAY_ID);
trigger(new NettyInit(222, false, VodMsgFrameDecoder.class),
client.getControl());
PortBindRequest pb1 = new PortBindRequest(clientAddr.getPeerAddress(),
Transport.UDP);
PortBindResponse pbr1 = new PortBindResponse(pb1) { };
trigger(pb1, client.getPositive(NatNetworkControl.class));
trigger(new NettyInit(99, false, VodMsgFrameDecoder.class),
client2.getControl());
pb1 = new PortBindRequest(client2Addr.getPeerAddress(), Transport.UDP);
pbr1 = new PortBindResponse(pb1) { };
trigger(pb1, client2.getPositive(NatNetworkControl.class));
trigger(new NettyInit(77, false, VodMsgFrameDecoder.class),
server.getControl());
pb1 = new PortBindRequest(serverAddr.getPeerAddress(), Transport.UDP);
pbr1 = new PortBindResponse(pb1) { };
trigger(pb1, server.getPositive(NatNetworkControl.class));
// EncodedSubPieceTest
HTTPStreamingClient stream = null;
fecs = new HashMap<Integer, VideoFEC>();
decodeFecs = new HashMap<Integer, VideoFEC>();
decodeFecs2 = new HashMap<Integer, VideoFEC>();
random = new Random();
try {
URL srcUrl = ClassLoader.getSystemClassLoader().getResource("source.mp4");
File sourceFile = new File(srcUrl.toURI());
stream = new HTTPStreamingClient(sourceFile);
stream.run();
} catch (URISyntaxException ex) {
java.util.logging.Logger.getLogger(EncodedSubPieceTest.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
java.util.logging.Logger.getLogger(EncodedSubPieceTest.class.getName()).log(Level.SEVERE, null, ex);
}
while (stream.hasNextPiece()) {
VideoFEC fec = new VideoFEC(stream.getNextPiece());
fecs.put(fec.getId(), fec);
}
numberOfTestSubPieces = fecs.size() * Piece.SUBPIECES;
numberOfTestSubPiecesLeft = numberOfTestSubPieces;
numberOfTestSubPiecesLeft2 = numberOfTestSubPieces;
Set<EncodedSubPiece> pieceSet;
System.out.println("Sending " + numberOfTestSubPieces + " sub-pieces");
for (VideoFEC fec : fecs.values()) {
// Send in random order and drop some packets
List<Integer> ids = new ArrayList<Integer>();
for (int i = 0; i < fec.getEncodedSubPieces().length; i++) {
ids.add(i);
}
for (int i = fec.getEncodedSubPieces().length - 1; i >= 0; i--) {
// if (i % 30 == 0) {
// continue;
// }
EncodedSubPiece esp = fec.getEncodedSubPiece(ids.remove(random.nextInt(ids.size())));
VideoPieceMsg.Response piecesMsg = new VideoPieceMsg.Response(serverAddr, clientAddr, UUID.nextUUID(), esp);
trigger(piecesMsg, server.getPositive(VodNetwork.class));
try {
// Do not overload the receiver's buffer
Thread.sleep(5);
} catch (InterruptedException ex) {
java.util.logging.Logger.getLogger(EncodedSubPieceTest.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
System.out.println("Source: done sending.");
}
};
public Handler<VideoPieceMsg.Request> handleConnectRequest = new Handler<VideoPieceMsg.Request>() {
@Override
public void handle(VideoPieceMsg.Request r) {
System.out.println("Piece Request");
}
};
public Handler<VideoPieceMsg.Response> handleDataResponse = new Handler<VideoPieceMsg.Response>() {
@Override
public void handle(VideoPieceMsg.Response r) {
int myId = r.getDestination().getId();
EncodedSubPiece esp = r.getEncodedSubPiece();
handleEncodedSubPiece(esp, myId);
if (myId == clientAddr.getId()) {
if (--numberOfTestSubPiecesLeft == 0) {
System.out.println(myId + ": All sub-pieces received, writing to file.");
List<Piece> pieces = new ArrayList<Piece>();
for (VideoFEC fec : fecs.values()) {
pieces.add(fec.getPiece());
}
// try {
// File destFile = new File("EncodedSubPieceTest" + myId + ".mp4");
// destFile.deleteOnExit();
// PieceHandler.writePieceData(destFile.getName(), pieces);
// assertEquals(new File("source.mp4").length(), destFile.length());
// } catch (IOException ex) {
// java.util.logging.Logger.getLogger(EncodedSubPieceTest.class.getName()).log(Level.SEVERE, null, ex);
// }
System.out.println(myId + ": Done, waiting for second client.");
}
} else if (myId == client2Addr.getId()) {
if (--numberOfTestSubPiecesLeft2 == 0) {
System.out.println(myId + ": All sub-pieces received, writing to file.");
List<Piece> pieces = new ArrayList<Piece>();
for (VideoFEC fec : fecs.values()) {
pieces.add(fec.getPiece());
}
// try {
// File destFile = new File("EncodedSubPieceTest" + myId + ".mp4");
// destFile.deleteOnExit();
// PieceHandler.writePieceData(destFile.getName(), pieces);
// assertEquals(new File("source.mp4").length(), destFile.length());
// } catch (IOException ex) {
// java.util.logging.Logger.getLogger(EncodedSubPieceTest.class.getName()).log(Level.SEVERE, null, ex);
// }
System.out.println(myId + ": Done.");
// Clean up
fecs.clear();
decodeFecs.clear();
decodeFecs2.clear();
trigger(new Stop(), client.getControl());
trigger(new Stop(), client2.getControl());
trigger(new Stop(), server.getControl());
testObj.pass();
}
}
}
};
public Handler<VideoPieceMsg.RequestTimeout> handleMsgTimeout = new Handler<VideoPieceMsg.RequestTimeout>() {
@Override
public void handle(VideoPieceMsg.RequestTimeout rt) {
trigger(new Stop(), client.getControl());
trigger(new Stop(), server.getControl());
System.out.println("Msg timeout");
testObj.testStatus = false;
testObj.fail(true);
}
};
public void handleEncodedSubPiece(EncodedSubPiece esp, int nodeId) {
if (nodeId == clientAddr.getId()) {
VideoFEC decodeFec = decodeFecs.get(esp.getParentId());
if (decodeFec == null) {
decodeFec = new VideoFEC(esp.getParentId());
decodeFecs.put(decodeFec.getId(), decodeFec);
}
if (!decodeFec.isReady()) {
decodeFec.addEncodedSubPiece(esp);
if (decodeFec.isReady()) {
Piece decodedPiece = decodeFec.decode();
// compare to original
VideoFEC encodeFec = fecs.get(decodeFec.getId());
Piece piece = encodeFec.getPiece();
for (SubPiece sp : piece.getSubPieces()) {
SubPiece dsp = decodedPiece.getSubPieces()[sp.getId()];
if (!sp.equals(dsp)) {
System.out.println(nodeId + ": decoded SubPiece " + dsp.getId() + " in Piece " + dsp.getParent().getId() + " does not equal original ("
+ sp.getId() + ")"
+ " - " + new String(Arrays.copyOf(dsp.getData(), 32), Charset.forName("US-ASCII"))
+ " != " + new String(Arrays.copyOf(sp.getData(), 32), Charset.forName("US-ASCII")));
}
}
assertEquals(piece, decodedPiece);
VideoFEC reencodeFec = new VideoFEC(decodedPiece);
assertTrue(Arrays.deepEquals(encodeFec.getEncodedSubPieces(), reencodeFec.getEncodedSubPieces()));
// Send to second client
Set<EncodedSubPiece> pieceSet;
// Send in reverse order and drop some packets
for (int i = encodeFec.getEncodedSubPieces().length - 1; i >= 0; i--) {
if (i % 25 == 0) {
continue;
}
esp = encodeFec.getEncodedSubPiece(i);
VideoPieceMsg.Response piecesMsg = new VideoPieceMsg.Response(clientAddr, client2Addr, UUID.nextUUID(), esp);
trigger(piecesMsg, client.getPositive(VodNetwork.class));
try {
// Do not overload the receiver's buffer
Thread.sleep(5);
} catch (InterruptedException ex) {
java.util.logging.Logger.getLogger(EncodedSubPieceTest.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
} else if (nodeId == client2Addr.getId()) {
VideoFEC decodeFec = decodeFecs2.get(esp.getParentId());
if (decodeFec == null) {
decodeFec = new VideoFEC(esp.getParentId());
decodeFecs2.put(decodeFec.getId(), decodeFec);
}
if (!decodeFec.isReady()) {
decodeFec.addEncodedSubPiece(esp);
if (decodeFec.isReady()) {
Piece decodedPiece = decodeFec.decode();
// compare to original
VideoFEC encodeFec = fecs.get(decodeFec.getId());
Piece piece = encodeFec.getPiece();
assertEquals(piece, decodedPiece);
VideoFEC reencodeFec = new VideoFEC(decodedPiece);
assertTrue(Arrays.deepEquals(encodeFec.getEncodedSubPieces(), reencodeFec.getEncodedSubPieces()));
}
}
}
}
}
private static final int EVENT_COUNT = 1;
private static Semaphore semaphore = new Semaphore(0);
private void allTests() {
int i = 0;
runInstance();
if (testStatus == true) {
assertTrue(true);
}
}
private void runInstance() {
Kompics.createAndStart(EncodedSubPieceTest.TestStClientComponent.class, 1);
try {
EncodedSubPieceTest.semaphore.acquire(EVENT_COUNT);
System.out.println("Finished test.");
} catch (InterruptedException e) {
assert (false);
} finally {
Kompics.shutdown();
}
if (testStatus == false) {
assertTrue(false);
}
}
@Ignore
public void testApp() {
setTestObj(this);
// allTests();
}
public void pass() {
EncodedSubPieceTest.semaphore.release();
}
public void fail(boolean release) {
testStatus = false;
EncodedSubPieceTest.semaphore.release();
}
}