package com.limegroup.gnutella.udpconnect;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import junit.framework.Test;
import com.limegroup.gnutella.Acceptor;
import com.limegroup.gnutella.ByteReader;
import com.limegroup.gnutella.Constants;
import com.limegroup.gnutella.ErrorService;
import com.limegroup.gnutella.RouterService;
import com.limegroup.gnutella.settings.ConnectionSettings;
import com.limegroup.gnutella.stubs.ActivityCallbackStub;
import com.limegroup.gnutella.stubs.UDPServiceStub;
import com.limegroup.gnutella.util.BaseTestCase;
import com.limegroup.gnutella.util.IOUtils;
import com.limegroup.gnutella.util.ManagedThread;
/**
* Put full UDPConnection system through various tests.
*/
public final class UDPConnectionTest extends BaseTestCase {
private static RouterService rs;
private static Acceptor ac;
/*
* Constructs the test.
*/
public UDPConnectionTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(UDPConnectionTest.class);
}
/**
* Runs this test individually.
*/
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
public static void globalSetUp() throws Exception {
// Setup the test to use the UDPServiceStub
UDPConnectionProcessor.setUDPServiceForTesting(
UDPServiceStub.instance());
rs = new RouterService(new ActivityCallbackStub());
ac = rs.getAcceptor();
ac.setAddress(InetAddress.getByName("127.0.0.1"));
ac.setExternalAddress(InetAddress.getByName("127.0.0.1"));
ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false);
ConnectionSettings.FORCE_IP_ADDRESS.setValue(true);
ConnectionSettings.FORCED_IP_ADDRESS_STRING.setValue("127.0.0.1");
}
public static void globalTearDown() throws Exception {
// Cleanup the UDPServiceStub usage
UDPConnectionProcessor.setUDPServiceForTesting(null);
}
public void setUp() throws Exception {
ac.setAddress(InetAddress.getByName("127.0.0.1"));
ac.setExternalAddress(InetAddress.getByName("127.0.0.1"));
ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false);
ConnectionSettings.FORCE_IP_ADDRESS.setValue(true);
ConnectionSettings.FORCED_IP_ADDRESS_STRING.setValue("127.0.0.1");
// Add some simulated connections to the UDPServiceStub
UDPServiceStub.stubInstance().addReceiver(6346, 6348, 10, 0);
UDPServiceStub.stubInstance().addReceiver(6348, 6346, 10, 0);
}
public void tearDown() throws Exception {
// Clear out the receiver parameters for the UDPServiceStub
UDPServiceStub.stubInstance().clearReceivers();
}
/**
* Test that data can be written, echoed and read through the
* UDPConnections.
*
* @throws Exception if an error occurs
*/
public void testBasics() throws Exception {
final int NUM_BYTES = 20000;
// Start the second connection in another thread
// and run it to completion.
class Inner extends ManagedThread {
boolean sSuccess = false;
public void managedRun() {
yield();
try {
UDPConnection uconn2 =
new UDPConnection("127.0.0.1",6348);
sSuccess = UStandalone.echoServer(uconn2, NUM_BYTES);
} catch(IOException ioe) {
throw new RuntimeException(ioe);
}
}
public boolean getSuccess() {
return sSuccess;
}
}
Inner t = new Inner();
t.setName("EchoServer");
t.setDaemon(true);
t.start();
// Init the first connection
UDPConnection uconn1 = new UDPConnection("127.0.0.1",6346);
// Run the first connection
boolean cSuccess = UStandalone.echoClient(uconn1, NUM_BYTES);
// Wait for the second to finish
t.join();
// Get the success status of the second connection
boolean sSuccess = t.getSuccess();
// Validate the results
assertTrue("echoClient should return true ",
cSuccess);
assertTrue("echoServer should return true ",
sSuccess);
}
public void testBlockTransfers() throws Exception {
final int NUM_BLOCKS = 100;
// Start the second connection in another thread
// and run it to completion.
class Inner extends ManagedThread {
boolean sSuccess = false;
public void managedRun() {
yield();
try {
UDPConnection uconn2 =
new UDPConnection("127.0.0.1",6348);
sSuccess = UStandalone.echoServerBlock(uconn2, NUM_BLOCKS);
} catch(IOException ioe) {
throw new RuntimeException(ioe);
}
}
public boolean getSuccess() {
return sSuccess;
}
}
Inner t = new Inner();
t.setDaemon(true);
t.start();
// Init the first connection
UDPConnection uconn1 = new UDPConnection("127.0.0.1",6346);
// Run the first connection
boolean cSuccess = UStandalone.echoClientBlock(uconn1, NUM_BLOCKS);
// Wait for the second to finish
t.join();
// Get the success status of the second connection
boolean sSuccess = t.getSuccess();
// Validate the results
assertTrue("echoClient should return true ",
cSuccess);
assertTrue("echoServer should return true ",
sSuccess);
}
public void testOneWayTransfers() throws Exception {
final int NUM_BYTES = 20000;
// Start the second connection in another thread
// and run it to completion.
class Inner extends ManagedThread {
boolean sSuccess = false;
public void managedRun() {
yield();
try {
UDPConnection uconn2 =
new UDPConnection("127.0.0.1",6348);
sSuccess = UStandalone.unidirectionalServer(uconn2, NUM_BYTES);
} catch(IOException ioe) {
throw new RuntimeException(ioe);
}
}
public boolean getSuccess() {
return sSuccess;
}
}
Inner t = new Inner();
t.setDaemon(true);
t.start();
// Init the first connection
UDPConnection uconn1 = new UDPConnection("127.0.0.1",6346);
// Run the first connection
boolean cSuccess = UStandalone.unidirectionalClient(uconn1, NUM_BYTES);
// Wait for the second to finish
t.join();
// Get the success status of the second connection
boolean sSuccess = t.getSuccess();
// Validate the results
assertTrue("unidirectionalClient should return true ",
cSuccess);
assertTrue("unidirectionalServer should return true ",
sSuccess);
}
public void testIOUtilsOnStream() throws Exception {
// Start the second connection in another thread
UDPConnection uconn2;
ConnStarter t = new ConnStarter();
t.setDaemon(true);
t.start();
// Startup connection one in original thread
UDPConnection uconn1 = new UDPConnection("127.0.0.1",6346);
// Wait for commpletion of uconn2 startup
t.join();
// Get the initialized connection 2
uconn2 = t.getConnection();
// Output on the first connection
OutputStream ostream = uconn1.getOutputStream();
ostream.write("GET FOO BAR BLECK\r\nSecond Line\r\n".getBytes());
//uconn1.close();
// Read to end and one extra on second stream
InputStream istream = uconn2.getInputStream();
uconn2.setSoTimeout(Constants.TIMEOUT);
String word = IOUtils.readWord(istream,8);
uconn2.close();
// Validate the results
assertTrue("Read of word should be 'GET' - is:"+word,
"GET".equals(word));
}
public void testBufferedByteReader() throws Exception {
String line1 = "GET FOO BAR BLECK";
String line2 = "Second Line";
// Start the second connection in another thread
UDPConnection uconn2;
ConnStarter t = new ConnStarter();
t.setDaemon(true);
t.start();
// Startup connection one in original thread
UDPConnection uconn1 = new UDPConnection("127.0.0.1",6346);
// Wait for commpletion of uconn2 startup
t.join();
// Get the initialized connection 2
uconn2 = t.getConnection();
// Output on the first connection
OutputStream ostream = uconn1.getOutputStream();
ostream.write((line1+"\r\n"+line2+"\r\n").getBytes());
// Read to end and one extra on second stream
InputStream istream = uconn2.getInputStream();
uconn2.setSoTimeout(Constants.TIMEOUT);
BufferedInputStream bistream = new BufferedInputStream(istream);
ByteReader br = new ByteReader(bistream);
String line = br.readLine();
uconn1.close();
uconn2.close();
// Validate the results
assertTrue("Read of line should be:"+line1,
line1.equals(line));
}
public void testReadBeyondEnd() throws Exception {
final int NUM_BYTES = 100;
// Start the second connection in another thread
UDPConnection uconn2;
ConnStarter t = new ConnStarter();
t.setDaemon(true);
t.start();
// Startup connection one in original thread
UDPConnection uconn1 = new UDPConnection("127.0.0.1",6346);
// Wait for commpletion of uconn2 startup
t.join();
// Get the initialized connection 2
uconn2 = t.getConnection();
// Output on the first connection
OutputStream ostream = uconn1.getOutputStream();
for ( int i = 0; i < NUM_BYTES; i++ )
ostream.write(i % 256);
// Read to end and one extra on second stream
InputStream istream = uconn2.getInputStream();
int rval;
for ( int i = 0; i < NUM_BYTES; i++ ) {
rval = istream.read();
if ( (i % 256) != rval )
fail("Error on byte:"+i);
}
// Close writer
uconn1.close();
// Wait a little
try { Thread.sleep(200); } catch (InterruptedException e) {}
// Read from reader
rval = istream.read();
// Validate the results
assertEquals("Read at end of stream should be -1",
rval, -1);
}
public void testReadBeyondEndAsBlock() throws Exception {
final int NUM_BYTES = 100;
// Start the second connection in another thread
UDPConnection uconn2;
ConnStarter t = new ConnStarter();
t.setDaemon(true);
t.start();
// Startup connection one in original thread
final UDPConnection uconn1 = new UDPConnection("127.0.0.1",6346);
// Wait for commpletion of uconn2 startup
t.join();
// Get the initialized connection 2
uconn2 = t.getConnection();
// Output on the first connection
OutputStream ostream = uconn1.getOutputStream();
for ( int i = 0; i < NUM_BYTES; i++ )
ostream.write(i % 256);
// Let data get sent to reader
Thread.sleep(500);
// Close writer
uconn1.close();
// Read to end and one extra on second stream
InputStream istream = uconn2.getInputStream();
byte bdata[] = new byte[512];
int rval;
int i = 0;
while (true) {
int len = istream.read(bdata);
for ( int j = 0; j < len; j++ ) {
rval = (int)bdata[j] & 0xff;
if ( (i % 256) != rval )
fail("Error on byte:"+i);
i++;
}
if (i >= NUM_BYTES)
break;
}
// Read from reader
rval = istream.read(bdata);
// Validate the results
assertEquals("Read at end of stream should be -1",
rval, -1);
}
public void testReadBeyondEndAsBlockDuringRead() throws Exception {
final int NUM_BYTES = 100;
// Start the second connection in another thread
UDPConnection uconn2;
ConnStarter t = new ConnStarter();
t.setDaemon(true);
t.start();
// Startup connection one in original thread
final UDPConnection uconn1 = new UDPConnection("127.0.0.1",6346);
// Wait for commpletion of uconn2 startup
t.join();
// Get the initialized connection 2
uconn2 = t.getConnection();
// Output on the first connection
OutputStream ostream = uconn1.getOutputStream();
for ( int i = 0; i < NUM_BYTES; i++ )
ostream.write(i % 256);
// Close the writer while the reader is blocked
class Inner extends ManagedThread {
public void managedRun() {
try {
// Let reader lock up on block read
Thread.sleep(500);
// Close writer
uconn1.close();
} catch(IOException e) {
} catch(InterruptedException ie) {
}
}
}
Inner st = new Inner();
st.setDaemon(true);
st.start();
// Read to end and one extra on second stream
InputStream istream = uconn2.getInputStream();
byte bdata[] = new byte[512];
int rval;
int i = 0;
while (true) {
int len = istream.read(bdata);
for ( int j = 0; j < len; j++ ) {
rval = (int)bdata[j] & 0xff;
if ( (i % 256) != rval )
fail("Error on byte:"+i);
i++;
}
if (i >= NUM_BYTES)
break;
}
// Read from reader
rval = istream.read(bdata);
// Validate the results
assertEquals("Read at end of stream should be -1",
rval, -1);
}
/**
* Test that data can be written, echoed and read through flaky
* UDPConnections.
*
* @throws Exception if an error occurs
*/
public void testFlakyConnection() throws Exception {
final int NUM_BYTES = 200000;
// Clear out my standard setup
UDPServiceStub.stubInstance().clearReceivers();
// Add some simulated connections to the UDPServiceStub
// Make the connections 5% flaky
UDPServiceStub.stubInstance().addReceiver(6346, 6348, 10, 5);
UDPServiceStub.stubInstance().addReceiver(6348, 6346, 10, 5);
// Start the second connection in another thread
// and run it to completion.
class Inner extends ManagedThread {
boolean sSuccess = false;
public void managedRun() {
yield();
try {
UDPConnection uconn2 =
new UDPConnection("127.0.0.1",6348);
sSuccess = UStandalone.echoServer(uconn2, NUM_BYTES);
} catch(IOException ioe) {
throw new RuntimeException(ioe);
}
}
public boolean getSuccess() {
return sSuccess;
}
}
Inner t = new Inner();
t.setDaemon(true);
t.start();
// Init the first connection
UDPConnection uconn1 = new UDPConnection("127.0.0.1",6346);
// Run the first connection
boolean cSuccess = UStandalone.echoClient(uconn1, NUM_BYTES);
// Wait for the second to finish
t.join();
// Get the success status of the second connection
boolean sSuccess = t.getSuccess();
// Validate the results
assertTrue("echoClient should return true ",
cSuccess);
assertTrue("echoServer should return true ",
sSuccess);
}
/**
* Test that data can be written, echoed and read through
* an extrely flaky UDPConnection where 15% of messages are lost.
*
* @throws Exception if an error occurs
*/
public void testExtremelyFlakyConnection() throws Exception {
final int NUM_BYTES = 20000;
// Clear out my standard setup
UDPServiceStub.stubInstance().clearReceivers();
// Add some simulated connections to the UDPServiceStub
// Make the connections 15% flaky
UDPServiceStub.stubInstance().addReceiver(6346, 6348, 10, 10);
UDPServiceStub.stubInstance().addReceiver(6348, 6346, 10, 10);
// Start the second connection in another thread
// and run it to completion.
class Inner extends ManagedThread {
boolean sSuccess = false;
public void managedRun() {
yield();
try {
UDPConnection uconn2 =
new UDPConnection("127.0.0.1",6348);
sSuccess = UStandalone.echoServer(uconn2, NUM_BYTES);
} catch(IOException ioe) {
throw new RuntimeException(ioe);
}
}
public boolean getSuccess() {
return sSuccess;
}
}
Inner t = new Inner();
t.setDaemon(true);
t.start();
// Init the first connection
UDPConnection uconn1 = new UDPConnection("127.0.0.1",6346);
// Run the first connection
boolean cSuccess = UStandalone.echoClient(uconn1, NUM_BYTES);
// Wait for the second to finish
t.join();
// Get the success status of the second connection
boolean sSuccess = t.getSuccess();
// Validate the results
assertTrue("echoClient should return true ",
cSuccess);
assertTrue("echoServer should return true ",
sSuccess);
}
/**
* Test UDPConnections with a very long delay.
*
* @throws Exception if an error occurs
*/
public void testExtremelySlowConnection() throws Exception {
final int NUM_BYTES = 60000;
// Clear out my standard setup
UDPServiceStub.stubInstance().clearReceivers();
// Add some simulated connections to the UDPServiceStub
// Make the connections 25% flaky
UDPServiceStub.stubInstance().addReceiver(6346, 6348, 1000, 0);
UDPServiceStub.stubInstance().addReceiver(6348, 6346, 1000, 0);
// Start the second connection in another thread
// and run it to completion.
class Inner extends ManagedThread {
boolean sSuccess = false;
public void managedRun() {
yield();
try {
UDPConnection uconn2 =
new UDPConnection("127.0.0.1",6348);
sSuccess = UStandalone.echoServer(uconn2, NUM_BYTES);
} catch(IOException ioe) {
throw new RuntimeException(ioe);
}
}
public boolean getSuccess() {
return sSuccess;
}
}
Inner t = new Inner();
t.setDaemon(true);
t.start();
// Init the first connection
UDPConnection uconn1 = new UDPConnection("127.0.0.1",6346);
// Run the first connection
boolean cSuccess = UStandalone.echoClient(uconn1, NUM_BYTES);
// Wait for the second to finish
t.join();
// Get the success status of the second connection
boolean sSuccess = t.getSuccess();
// Validate the results
assertTrue("echoClient should return true ",
cSuccess);
assertTrue("echoServer should return true ",
sSuccess);
}
/**
* Startup a second UDPConnection in a thread since two connections will
* block if started in one thread.
*/
class ConnStarter extends ManagedThread {
UDPConnection uconn2;
public ConnStarter() {
}
public void managedRun() {
yield();
try {
uconn2 =
new UDPConnection("127.0.0.1",6348);
} catch (IOException ioe) {
ErrorService.error(ioe);
}
}
public UDPConnection getConnection() {
return uconn2;
}
}
}