// BlogBridge -- RSS feed reader, manager, and web based service
// Copyright (C) 2002-2006 by R. Pito Salas
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software Foundation;
// either version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with this program;
// if not, write to the Free Software Foundation, Inc., 59 Temple Place,
// Suite 330, Boston, MA 02111-1307 USA
//
// Contact: R. Pito Salas
// mailto:pitosalas@users.sourceforge.net
// More information: about BlogBridge
// http://www.blogbridge.com
// http://sourceforge.net/projects/blogbridge
//
// $Id: TestURLInputStream.java,v 1.9 2006/01/08 05:28:18 kyank Exp $
//
package com.salas.bb.utils.net;
import junit.framework.TestCase;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @see URLInputStream
*/
public class TestURLInputStream extends TestCase
{
// Number of seconds to last each test.
private static final int BANDWIDTH_TEST_SEC = 3;
// Maximum error in seconds for the bandwidth limitation tests.
private static final double BANDWIDTH_TEST_ERROR = 1;
private int testResourceSize;
public TestURLInputStream()
throws IOException
{
URL url = getTestURL();
testResourceSize = url.openConnection().getContentLength();
}
/**
* Tests basic creation.
*/
public void testCreation()
throws IOException
{
URLInputStream uis = null;
try
{
uis = new URLInputStream(null);
fail("NULL-URL's aren't allowed.");
} catch (Exception e)
{
// Expected
} finally
{
if (uis != null) uis.close();
}
uis = new URLInputStream(new URL("file:/test123"));
try
{
uis.available();
fail("File should not be found.");
} catch (IOException e)
{
// Expected - file not found
} finally
{
uis.close();
}
try
{
uis = new URLInputStream(getTestURL());
assertTrue(uis.available() > 0);
} catch (IOException e)
{
e.printStackTrace();
fail();
} finally
{
uis.close();
}
}
/**
* Checks unlimited bandwidth reading.
*/
public void testBandwidthUnlimited()
throws IOException
{
URL url = getTestURL();
URLInputStream uis = new URLInputStream(url);
int read = 0;
long startTime = System.currentTimeMillis();
while (uis.read() != -1) read++;
long finishTime = System.currentTimeMillis();
assertEquals(testResourceSize, read);
assertTrue((finishTime - startTime) < 1000);
uis.close();
}
/**
* Checks limited bandwidth reading (byte-per read).
*/
public void testBandwidthLimitedSingleStep()
throws IOException
{
URL url = getTestURL();
URLInputStream uis = new URLInputStream(url);
uis.setBandwidth(testResourceSize / BANDWIDTH_TEST_SEC);
int read = 0;
long startTime = System.currentTimeMillis();
while (uis.read() != -1) read++;
long finishTime = System.currentTimeMillis();
final long readTime = (finishTime - startTime);
assertEquals(testResourceSize, read);
assertTrue(Long.toString(readTime), readTime < (BANDWIDTH_TEST_SEC + BANDWIDTH_TEST_ERROR) * 1000);
assertTrue(Long.toString(readTime), readTime > (BANDWIDTH_TEST_SEC - BANDWIDTH_TEST_ERROR) * 1000);
uis.close();
}
/**
* Checks limited bandwidth reading in blocks to buffer w/o offset and length.
*/
public void testBandwidthLimitedBlockA()
throws IOException
{
URL url = getTestURL();
URLInputStream uis = new URLInputStream(url);
uis.setBandwidth(testResourceSize / BANDWIDTH_TEST_SEC);
int read = 0;
byte[] buf = new byte[1000];
long rd;
long startTime = System.currentTimeMillis();
while ((rd = uis.read(buf)) != -1) read += rd;
long finishTime = System.currentTimeMillis();
final long readTime = (finishTime - startTime);
assertEquals(testResourceSize, read);
assertTrue(Long.toString(readTime), readTime < (BANDWIDTH_TEST_SEC + BANDWIDTH_TEST_ERROR) * 1000);
assertTrue(Long.toString(readTime), readTime > (BANDWIDTH_TEST_SEC - BANDWIDTH_TEST_ERROR) * 1000);
uis.close();
}
/**
* Checks limited bandwidth reading in blocks to buffer w/ offset and length specified.
*/
public void testBandwidthLimitedBlockB()
throws IOException
{
URL url = getTestURL();
URLInputStream uis = new URLInputStream(url);
uis.setBandwidth(testResourceSize / BANDWIDTH_TEST_SEC);
int read = 0;
byte[] buf = new byte[1000];
long rd;
long startTime = System.currentTimeMillis();
while ((rd = uis.read(buf, 0, 10)) != -1) read += rd;
long finishTime = System.currentTimeMillis();
final long readTime = (finishTime - startTime);
assertEquals(testResourceSize, read);
assertTrue(Long.toString(readTime), readTime < (BANDWIDTH_TEST_SEC + BANDWIDTH_TEST_ERROR) * 1000);
assertTrue(Long.toString(readTime), readTime > (BANDWIDTH_TEST_SEC - BANDWIDTH_TEST_ERROR) * 1000);
uis.close();
}
/**
* Tests how the pauses in reading are taken.
*/
public void testBandwidthThreshold()
throws IOException
{
long delay = 2;
URL url = getTestURL();
URLInputStream uis = new URLInputStream(url);
uis.setBandwidth(testResourceSize / BANDWIDTH_TEST_SEC);
long read = 0;
long rd;
byte[] buf = new byte[1000];
long startTime = System.currentTimeMillis();
uis.read();
read++;
try
{
Thread.sleep(delay * 1000);
} catch (InterruptedException e)
{
}
while ((rd = uis.read(buf)) != -1) read += rd;
long finishTime = System.currentTimeMillis();
final long readTime = (finishTime - startTime);
assertEquals(testResourceSize, read);
assertTrue(Long.toString(readTime), readTime < (BANDWIDTH_TEST_SEC + BANDWIDTH_TEST_ERROR + delay) * 1000);
assertTrue(Long.toString(readTime), readTime > (BANDWIDTH_TEST_SEC - BANDWIDTH_TEST_ERROR + delay) * 1000);
uis.close();
}
/**
* Tests the changing of bandwidth on-the-fly.
*/
public void testRealtimeBandwidthChanging()
throws IOException
{
URL url = getTestURL();
URLInputStream uis = new URLInputStream(url);
int read = 0;
long startTime = System.currentTimeMillis();
// Read one byte with unlimited bandwidth usage
assertTrue(uis.read() != -1);
read++;
// Change bandwidth limit
uis.setBandwidth(testResourceSize / BANDWIDTH_TEST_SEC);
// Read all the rest and measure the total time
while (uis.read() != -1) read++;
long finishTime = System.currentTimeMillis();
final long readTime = (finishTime - startTime);
assertEquals(testResourceSize, read);
assertTrue(Long.toString(readTime), readTime < (BANDWIDTH_TEST_SEC + BANDWIDTH_TEST_ERROR) * 1000);
assertTrue(Long.toString(readTime), readTime > (BANDWIDTH_TEST_SEC - BANDWIDTH_TEST_ERROR) * 1000);
uis.close();
}
/**
* Tests how the skipping works.
*/
public void testSkipping()
throws IOException
{
URL url = getTestURL();
URLInputStream uis = new URLInputStream(url);
int blockSize = testResourceSize / 3;
int[] blocks = new int[] { blockSize, blockSize, blockSize, testResourceSize - blockSize * 3 };
int read = 0;
byte[] bl = new byte[blockSize];
for (int i = 0; i < blocks.length; i++)
{
int block = blocks[i];
while (block > 0)
{
if (i % 2 == 0)
{
block -= uis.read(bl, 0, block);
} else
{
block -= (int)uis.skip(block);
}
}
read += blocks[i];
}
assertEquals(testResourceSize, read);
uis.close();
}
/**
* Tests how the stream is automatically closing on completion.
*/
public void testAutoClosing()
throws IOException
{
URL url = getTestURL();
URLInputStream uis = new URLInputStream(url);
int size = testResourceSize;
while (size > 0)
{
size -= uis.skip(size);
}
assertEquals(-1, uis.read());
assertEquals(-1, uis.read());
assertEquals(-1, uis.read(new byte[10]));
assertEquals(-1, uis.read(new byte[10]));
assertEquals(-1, uis.read(new byte[10], 0, 1));
assertEquals(-1, uis.read(new byte[10], 0, 1));
// Skipping skips even when stream has ended
assertEquals(1, uis.skip(1));
uis.close();
}
/**
* @see URLInputStream#handleFailure
*/
public void testHandleFailure() throws IOException
{
IRetriesPolicy.Failure failure;
URLInputStream uis = new URLInputStream(getTestURL());
uis.setRetriesPolicy(new DirectRetriesPolicy(1, 0));
failure = new IRetriesPolicy.Failure(0, 0, 0, false, 0, new IOException("Failed"));
uis.handleFailure(failure);
failure = new IRetriesPolicy.Failure(1, 0, 0, false, 0, new IOException("Failed"));
try
{
uis.handleFailure(failure);
fail("Handling should fail. Attempts ran out.");
} catch (IOException e)
{
// Expected
assertEquals("Failed", e.getMessage());
}
uis.close();
}
/**
* @see URLInputStream#connectionAttempt
*/
public void testConnectionAttempt() throws IOException
{
IRetriesPolicy.Failure failure;
DirectRetriesPolicy retriesPolicy = new DirectRetriesPolicy(1, 0);
URL testURL = getTestURL();
URLInputStream uis = new URLInputStream(testURL)
{
// Always fails.
protected InputStream makeConnection(long read)
throws IOException
{
throw new IOException("Failed");
}
};
uis.setRetriesPolicy(retriesPolicy);
long start = System.currentTimeMillis();
failure = uis.connectionAttempt(2);
long finish = System.currentTimeMillis();
assertEquals(2, failure.getAttemptNumber());
assertTrue(failure.getAttemptStartTime() >= start && failure.getAttemptStartTime() <= finish);
assertTrue(failure.getAttemptFailureTime() >= start && failure.getAttemptFailureTime() <= finish);
assertEquals(0, failure.getBytesRead());
assertEquals("Failed", failure.getCause().getMessage());
uis.close();
BandwidthInputStream stream;
// Default bandwidth and success
uis = new URLInputStream(testURL);
uis.setRetriesPolicy(retriesPolicy);
failure = uis.connectionAttempt(0);
stream = uis.getStream();
assertNull(failure);
assertNotNull(stream);
assertEquals(0, stream.getBandwidth());
uis.close();
// Custom bandwidth and success
uis = new URLInputStream(testURL);
uis.setRetriesPolicy(retriesPolicy);
uis.setBandwidth(1000);
failure = uis.connectionAttempt(0);
stream = uis.getStream();
assertNull(failure);
assertNotNull(stream);
assertEquals(uis.getBandwidth(), stream.getBandwidth());
uis.close();
}
/**
* @see URLInputStream#connect
*/
public void testConnectToClosedStream() throws IOException
{
URLInputStream uis = new URLInputStream(getTestURL());
uis.close();
try
{
uis.read();
fail("IOException should be thrown. The stream is already closed.");
} catch (IOException e)
{
// Expected
}
}
/**
* @see URLInputStream#connect
*/
public void testRepeatableConnection() throws IOException
{
// Passes first connection and then fails all the way.
URLInputStream uis = new URLInputStream(getTestURL())
{
private int cnt = 0;
protected InputStream makeConnection(long read)
throws IOException
{
if (cnt++ > 0) throw new IOException("Failed");
return super.makeConnection(read);
}
};
// Connects and read byte
assertTrue(uis.read() != -1);
// If this one will require another connection we will fail.
assertTrue(uis.read() != -1);
uis.close();
}
/**
* @see URLInputStream#connect
*/
public void testSecondAttemptConnection() throws IOException
{
// Fails first attempt and then passes all the way.
URLInputStream uis = new URLInputStream(getTestURL())
{
private int cnt = 0;
protected InputStream makeConnection(long read)
throws IOException
{
if (cnt++ < 1) throw new IOException("Failed");
return super.makeConnection(read);
}
};
// Connects and read byte
assertTrue(uis.read() != -1);
uis.close();
}
/**
* Checks the resuming during reading byte.
*/
public void testResumeReadingByte()
throws IOException
{
Logger logger = Logger.getLogger(URLInputStream.class.getName());
logger.setLevel(Level.OFF);
// Fails to read tenth byte once
URLInputStream uis = new URLInputStream(getTestURL())
{
private boolean failed = false;
protected InputStream makeConnection(long read)
throws IOException
{
InputStream in = super.makeConnection(read);
if (!failed)
{
in = new SingleFailInputStream(in, 10);
failed = true;
}
return in;
}
};
uis.setRetriesPolicy(new DirectRetriesPolicy(1, 0));
int size = 0;
while (uis.read() != -1) size++;
assertEquals(testResourceSize, size);
uis.close();
}
/**
* Checks the resuming during reading block.
*/
public void testResumeReadingBlock()
throws IOException
{
Logger logger = Logger.getLogger(URLInputStream.class.getName());
logger.setLevel(Level.OFF);
// Fails to read tenth byte once
URLInputStream uis = new URLInputStream(getTestURL())
{
private boolean failed = false;
protected InputStream makeConnection(long read)
throws IOException
{
InputStream in = super.makeConnection(read);
if (!failed)
{
in = new SingleFailInputStream(in, 10);
failed = true;
}
return in;
}
};
uis.setRetriesPolicy(new DirectRetriesPolicy(1, 0));
int size = 0;
byte[] buf = new byte[100];
int read;
while ((read = uis.read(buf)) != -1) size += read;
assertEquals(testResourceSize, size);
uis.close();
}
/**
* Checks the resuming during skipping.
*/
public void testResumeSkipping()
throws IOException
{
Logger logger = Logger.getLogger(URLInputStream.class.getName());
logger.setLevel(Level.OFF);
// Fails to read tenth byte once
URLInputStream uis = new URLInputStream(getTestURL())
{
private boolean failed = false;
protected InputStream makeConnection(long read)
throws IOException
{
InputStream in = super.makeConnection(read);
if (!failed)
{
in = new SingleFailInputStream(in, 10);
failed = true;
}
return in;
}
};
uis.setRetriesPolicy(new DirectRetriesPolicy(1, 0));
int size = testResourceSize;
while (size > 0) size -= uis.skip(size);
uis.close();
}
/**
* Tests reporting the progress when reading streams in byte-size steps.
*/
public void testProgressReadingBytes() throws IOException
{
Logger logger = Logger.getLogger(URLInputStream.class.getName());
logger.setLevel(Level.OFF);
RecordingStreamListener listener = new RecordingStreamListener();
URLInputStream uis = new URLInputStream(getTestURL());
uis.addListener(listener);
while (uis.read() != -1);
assertTrue(listener.connecting);
assertTrue(listener.connected);
assertTrue(listener.finished);
assertTrue(listener.size > 0);
assertEquals(listener.size, listener.read);
uis.close();
}
/**
* Tests reporting the progress when reading streams in block steps.
*/
public void testProgressReadingBlocks() throws IOException
{
Logger logger = Logger.getLogger(URLInputStream.class.getName());
logger.setLevel(Level.OFF);
RecordingStreamListener listener = new RecordingStreamListener();
URLInputStream uis = new URLInputStream(getTestURL());
uis.addListener(listener);
byte[] buf = new byte[100];
while (uis.read(buf) != -1);
assertTrue(listener.connecting);
assertTrue(listener.connected);
assertTrue(listener.finished);
assertTrue(listener.size > 0);
assertEquals(listener.size, listener.read);
uis.close();
}
/**
* Tests reporting the progress when skipping streams.
*/
public void testProgressSkipping() throws IOException
{
Logger logger = Logger.getLogger(URLInputStream.class.getName());
logger.setLevel(Level.OFF);
RecordingStreamListener listener = new RecordingStreamListener();
URLInputStream uis = new URLInputStream(getTestURL());
uis.addListener(listener);
int size = testResourceSize;
while (size > 0) size -= uis.skip(size);
assertTrue(listener.connecting);
assertTrue(listener.connected);
assertTrue(listener.finished);
assertTrue(listener.size > 0);
assertEquals(listener.size, listener.read);
listener.finished = false;
uis.skip(1);
// Finished shouldn't be called twice
assertFalse(listener.finished);
assertEquals(listener.size, listener.read);
uis.close();
}
/**
* Tests reading of bytes in stream of undetermined length and how the finish is
* reported.
*/
public void testReportingFinishReadingBytesOfEndless() throws IOException
{
Logger logger = Logger.getLogger(URLInputStream.class.getName());
logger.setLevel(Level.OFF);
RecordingStreamListener listener = new RecordingStreamListener();
URLInputStream uis = new URLInputStream(getTestURL())
{
protected int resolveContentLength(URLConnection aCon)
{
// Always tell "undetermined".
return -1;
}
};
uis.addListener(listener);
while (uis.read() != -1);
assertTrue(listener.finished);
uis.close();
}
/**
* Tests reading of blocks in stream of undetermined length and how the finish is
* reported.
*/
public void testReportingFinishReadingBlocksOfEndless() throws IOException
{
Logger logger = Logger.getLogger(URLInputStream.class.getName());
logger.setLevel(Level.OFF);
RecordingStreamListener listener = new RecordingStreamListener();
URLInputStream uis = new URLInputStream(getTestURL())
{
protected int resolveContentLength(URLConnection aCon)
{
// Always tell "undetermined".
return -1;
}
};
uis.addListener(listener);
byte[] buf = new byte[100];
while (uis.read(buf) != -1);
assertTrue(listener.finished);
uis.close();
}
/**
* Tests how the UIS reports errored state during connection
* (for example, when file is missing).
*/
public void testFailureReportingDuringConnection() throws IOException
{
Logger logger = Logger.getLogger(URLInputStream.class.getName());
logger.setLevel(Level.OFF);
RecordingStreamListener listener = new RecordingStreamListener();
URLInputStream uis = new URLInputStream(new URL("file:///missing.file"));
uis.addListener(listener);
try
{
uis.read();
fail("File is missing -- exception should be thrown.");
} catch (IOException e)
{
// Expected
}
assertTrue(listener.connecting);
assertFalse(listener.connected);
assertFalse(listener.finished);
assertTrue(listener.errored);
uis.close();
}
/**
* Tests how the UIS reports errored state during reading.
*/
public void testFailureReportingDuringReading() throws IOException
{
Logger logger = Logger.getLogger(URLInputStream.class.getName());
logger.setLevel(Level.OFF);
RecordingStreamListener listener = new RecordingStreamListener();
URLInputStream uis = new URLInputStream(getTestURL());
uis.addListener(listener);
assertTrue(uis.read() != -1);
// Here we are cheating! Instead of honest simulation of several errors we simply
// close the stream and try to access it again which produces immediate error.
uis.close();
try
{
uis.read();
fail("The stream is closed -- exception should be thorwn.");
} catch (IOException e)
{
// Expected
}
assertTrue(listener.connecting);
assertTrue(listener.connected);
assertEquals(testResourceSize, listener.size);
assertEquals(1, listener.read);
assertTrue(listener.finished);
assertTrue(listener.errored);
}
/**
* Tests closing of stream even before connecting.
*/
public void testClosingBeforeOpening() throws IOException
{
URLInputStream uis = new URLInputStream(getTestURL());
uis.close();
try
{
uis.read();
fail("Connection is already closed. Cannot read.");
} catch (IOException e)
{
// Expected
}
}
/**
* Tests normal closing sequence.
*/
public void testClosing() throws IOException
{
URLInputStream uis = new URLInputStream(getTestURL());
assertTrue(uis.read() != -1);
uis.close();
try
{
uis.read();
fail("Stream is already closed. Cannot read.");
} catch (IOException e)
{
// Expected
}
try
{
uis.read(new byte[0]);
fail("Stream is already closed. Cannot read.");
} catch (IOException e)
{
// Expected
}
try
{
uis.read(new byte[1], 0, 1);
fail("Stream is already closed. Cannot read.");
} catch (IOException e)
{
// Expected
}
try
{
uis.available();
fail("Stream is already closed. Cannot read.");
} catch (IOException e)
{
// Expected
}
try
{
uis.skip(1);
fail("Stream is already closed. Cannot read.");
} catch (IOException e)
{
// Expected
}
}
/**
* Tests permanent redirection notifications.
*/
public void testRedirection()
throws IOException
{
URL url = new URL("http://www.blogbridge.com/weblog/index.xml");
PermRedirListener l = new PermRedirListener();
URLInputStream uis = new URLInputStream(url);
uis.setRedirectionListener(l);
assertTrue(uis.read() != -1);
assertTrue("Redirection didn't happen.", l.redirected);
uis.close();
}
// Returns test URL.
private URL getTestURL()
{
final String name = TestURLInputStream.class.getName().replaceAll("\\.", "/") + ".class";
final URL resource = TestURLInputStream.class.getClassLoader().getResource(name);
assertNotNull(resource);
return resource;
}
/**
* Simply records the fact of notification.
*/
private static class PermRedirListener implements IPermanentRedirectionListener
{
private boolean redirected;
public void redirectedTo(URL newLocation)
{
redirected = true;
}
}
/**
* Fails to read the specified byte in the stream once
*/
private static class SingleFailInputStream extends FilterInputStream
{
private int failByte;
private int read;
public SingleFailInputStream(InputStream in, int aFailByte)
{
super(in);
failByte = aFailByte;
read = 0;
}
public int read()
throws IOException
{
if (read++ == failByte)
{
throw new IOException("Connection terminated.");
}
return super.read();
}
public int read(byte b[], int off, int len)
throws IOException
{
if (read + len > failByte)
{
throw new IOException("Connection terminated.");
}
int rd = super.read(b, off, len);
if (rd != -1) read += rd;
return rd;
}
public long skip(long n)
throws IOException
{
if (read + n > failByte)
{
throw new IOException("Connection terminated.");
}
long sk = super.skip(n);
read += sk;
return sk;
}
}
/**
* Listener, recording whole reading process.
*/
private static class RecordingStreamListener implements IStreamProgressListener
{
private boolean connecting = false;
private boolean connected = false;
private long size = 0;
private long read = 0;
private boolean finished = false;
private boolean errored = false;
public void connecting(URLInputStream source)
{
assertFalse(connecting);
assertFalse(connected);
assertEquals(0, size);
assertEquals(0, read);
assertFalse(finished);
connecting = true;
// Throwing exception to test how we are actually protected against that.
throw new RuntimeException("Test");
}
public void connected(URLInputStream source, long length)
{
assertTrue(connecting);
assertFalse(connected);
assertEquals(0, size);
assertEquals(0, read);
assertFalse(finished);
connected = true;
size = length;
// Throwing exception to test how we are actually protected against that.
throw new RuntimeException("Test");
}
public void read(URLInputStream source, long bytes)
{
assertTrue(connecting);
assertTrue(connected);
assertFalse(finished);
read += bytes;
if (size != -1) assertTrue(read <= size);
// Throwing exception to test how we are actually protected against that.
throw new RuntimeException("Test");
}
public void finished(URLInputStream source)
{
assertTrue(connecting);
assertTrue(connected);
assertFalse(finished);
finished = true;
// Throwing exception to test how we are actually protected against that.
throw new RuntimeException("Test");
}
public void errored(URLInputStream source, IOException ex)
{
assertTrue(connecting);
errored = true;
// Throwing exception to test how we are actually protected against that.
throw new RuntimeException("Test");
}
}
}