blob: 490f1e6c5df793ae1cae01def2bcebc0c531d97a [file] [log] [blame]
/*
* Copyright (C) 2012 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.io;
import static com.google.common.io.SourceSinkFactory.ByteSourceFactory;
import static com.google.common.io.SourceSinkFactory.CharSourceFactory;
import static org.junit.Assert.assertArrayEquals;
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.Map.Entry;
import java.util.Random;
import junit.framework.TestSuite;
/**
* A generator of {@code TestSuite} instances for testing {@code ByteSource} implementations.
* Generates tests of a all methods on a {@code ByteSource} given various inputs the source is
* expected to contain as well as as sub-suites for testing the {@code CharSource} view and {@code
* slice()} views in the same way.
*
* @author Colin Decker
*/
@AndroidIncompatible // Android doesn't understand tests that lack default constructors.
public class ByteSourceTester extends SourceSinkTester<ByteSource, byte[], ByteSourceFactory> {
private static final ImmutableList<Method> testMethods = getTestMethods(ByteSourceTester.class);
static TestSuite tests(String name, ByteSourceFactory factory, boolean testAsCharSource) {
TestSuite suite = new TestSuite(name);
for (Entry<String, String> entry : TEST_STRINGS.entrySet()) {
if (testAsCharSource) {
suite.addTest(suiteForString(factory, entry.getValue(), name, entry.getKey()));
} else {
suite.addTest(
suiteForBytes(
factory, entry.getValue().getBytes(Charsets.UTF_8), name, entry.getKey(), true));
}
}
return suite;
}
static TestSuite suiteForString(
ByteSourceFactory factory, String string, String name, String desc) {
TestSuite suite = suiteForBytes(factory, string.getBytes(Charsets.UTF_8), name, desc, true);
CharSourceFactory charSourceFactory = SourceSinkFactories.asCharSourceFactory(factory);
suite.addTest(
CharSourceTester.suiteForString(
charSourceFactory, string, name + ".asCharSource[Charset]", desc));
return suite;
}
static TestSuite suiteForBytes(
ByteSourceFactory factory, byte[] bytes, String name, String desc, boolean slice) {
TestSuite suite = new TestSuite(name + " [" + desc + "]");
for (Method method : testMethods) {
suite.addTest(new ByteSourceTester(factory, bytes, name, desc, method));
}
if (slice && bytes.length > 0) {
// test a random slice() of the ByteSource
Random random = new Random();
byte[] expected = factory.getExpected(bytes);
// if expected.length == 0, off has to be 0 but length doesn't matter--result will be empty
int off = expected.length == 0 ? 0 : random.nextInt(expected.length);
int len = expected.length == 0 ? 4 : random.nextInt(expected.length - off);
ByteSourceFactory sliced = SourceSinkFactories.asSlicedByteSourceFactory(factory, off, len);
suite.addTest(suiteForBytes(sliced, bytes, name + ".slice[long, long]", desc, false));
// test a slice() of the ByteSource starting at a random offset with a length of
// Long.MAX_VALUE
ByteSourceFactory slicedLongMaxValue =
SourceSinkFactories.asSlicedByteSourceFactory(factory, off, Long.MAX_VALUE);
suite.addTest(
suiteForBytes(
slicedLongMaxValue, bytes, name + ".slice[long, Long.MAX_VALUE]", desc, false));
// test a slice() of the ByteSource starting at an offset greater than its size
ByteSourceFactory slicedOffsetPastEnd =
SourceSinkFactories.asSlicedByteSourceFactory(
factory, expected.length + 2, expected.length + 10);
suite.addTest(
suiteForBytes(slicedOffsetPastEnd, bytes, name + ".slice[size + 2, long]", desc, false));
}
return suite;
}
private ByteSource source;
public ByteSourceTester(
ByteSourceFactory factory, byte[] bytes, String suiteName, String caseDesc, Method method) {
super(factory, bytes, suiteName, caseDesc, method);
}
@Override
public void setUp() throws IOException {
source = factory.createSource(data);
}
public void testOpenStream() throws IOException {
InputStream in = source.openStream();
try {
byte[] readBytes = ByteStreams.toByteArray(in);
assertExpectedBytes(readBytes);
} finally {
in.close();
}
}
public void testOpenBufferedStream() throws IOException {
InputStream in = source.openBufferedStream();
try {
byte[] readBytes = ByteStreams.toByteArray(in);
assertExpectedBytes(readBytes);
} finally {
in.close();
}
}
public void testRead() throws IOException {
byte[] readBytes = source.read();
assertExpectedBytes(readBytes);
}
public void testCopyTo_outputStream() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
source.copyTo(out);
assertExpectedBytes(out.toByteArray());
}
public void testCopyTo_byteSink() throws IOException {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
// HERESY! but it's ok just for this I guess
source.copyTo(
new ByteSink() {
@Override
public OutputStream openStream() throws IOException {
return out;
}
});
assertExpectedBytes(out.toByteArray());
}
public void testIsEmpty() throws IOException {
assertEquals(expected.length == 0, source.isEmpty());
}
public void testSize() throws IOException {
assertEquals(expected.length, source.size());
}
public void testSizeIfKnown() throws IOException {
Optional<Long> sizeIfKnown = source.sizeIfKnown();
if (sizeIfKnown.isPresent()) {
assertEquals(expected.length, (long) sizeIfKnown.get());
}
}
public void testContentEquals() throws IOException {
assertTrue(
source.contentEquals(
new ByteSource() {
@Override
public InputStream openStream() throws IOException {
return new RandomAmountInputStream(
new ByteArrayInputStream(expected), new Random());
}
}));
}
public void testRead_usingByteProcessor() throws IOException {
byte[] readBytes =
source.read(
new ByteProcessor<byte[]>() {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
@Override
public boolean processBytes(byte[] buf, int off, int len) throws IOException {
out.write(buf, off, len);
return true;
}
@Override
public byte[] getResult() {
return out.toByteArray();
}
});
assertExpectedBytes(readBytes);
}
public void testHash() throws IOException {
HashCode expectedHash = Hashing.md5().hashBytes(expected);
assertEquals(expectedHash, source.hash(Hashing.md5()));
}
public void testSlice_illegalArguments() {
try {
source.slice(-1, 0);
fail("expected IllegalArgumentException for call to slice with offset -1: " + source);
} catch (IllegalArgumentException expected) {
}
try {
source.slice(0, -1);
fail("expected IllegalArgumentException for call to slice with length -1: " + source);
} catch (IllegalArgumentException expected) {
}
}
// Test that you can not expand the readable data in a previously sliced ByteSource.
public void testSlice_constrainedRange() throws IOException {
long size = source.read().length;
if (size >= 2) {
ByteSource sliced = source.slice(1, size - 2);
assertEquals(size - 2, sliced.read().length);
ByteSource resliced = sliced.slice(0, size - 1);
assertTrue(sliced.contentEquals(resliced));
}
}
private void assertExpectedBytes(byte[] readBytes) {
assertArrayEquals(expected, readBytes);
}
}