1 /* 2 * Copyright (C) 2012 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.io; 18 19 import static com.google.common.io.SourceSinkFactory.ByteSourceFactory; 20 import static com.google.common.io.SourceSinkFactory.CharSourceFactory; 21 import static org.junit.Assert.assertArrayEquals; 22 23 import com.google.common.base.Charsets; 24 import com.google.common.base.Optional; 25 import com.google.common.collect.ImmutableList; 26 import com.google.common.hash.HashCode; 27 import com.google.common.hash.Hashing; 28 import java.io.ByteArrayInputStream; 29 import java.io.ByteArrayOutputStream; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.OutputStream; 33 import java.lang.reflect.Method; 34 import java.util.Map.Entry; 35 import java.util.Random; 36 import junit.framework.TestSuite; 37 38 /** 39 * A generator of {@code TestSuite} instances for testing {@code ByteSource} implementations. 40 * Generates tests of a all methods on a {@code ByteSource} given various inputs the source is 41 * expected to contain as well as as sub-suites for testing the {@code CharSource} view and {@code 42 * slice()} views in the same way. 43 * 44 * @author Colin Decker 45 */ 46 @AndroidIncompatible // Android doesn't understand tests that lack default constructors. 47 public class ByteSourceTester extends SourceSinkTester<ByteSource, byte[], ByteSourceFactory> { 48 49 private static final ImmutableList<Method> testMethods = getTestMethods(ByteSourceTester.class); 50 tests(String name, ByteSourceFactory factory, boolean testAsCharSource)51 static TestSuite tests(String name, ByteSourceFactory factory, boolean testAsCharSource) { 52 TestSuite suite = new TestSuite(name); 53 for (Entry<String, String> entry : TEST_STRINGS.entrySet()) { 54 if (testAsCharSource) { 55 suite.addTest(suiteForString(factory, entry.getValue(), name, entry.getKey())); 56 } else { 57 suite.addTest( 58 suiteForBytes( 59 factory, entry.getValue().getBytes(Charsets.UTF_8), name, entry.getKey(), true)); 60 } 61 } 62 return suite; 63 } 64 suiteForString( ByteSourceFactory factory, String string, String name, String desc)65 static TestSuite suiteForString( 66 ByteSourceFactory factory, String string, String name, String desc) { 67 TestSuite suite = suiteForBytes(factory, string.getBytes(Charsets.UTF_8), name, desc, true); 68 CharSourceFactory charSourceFactory = SourceSinkFactories.asCharSourceFactory(factory); 69 suite.addTest( 70 CharSourceTester.suiteForString( 71 charSourceFactory, string, name + ".asCharSource[Charset]", desc)); 72 return suite; 73 } 74 suiteForBytes( ByteSourceFactory factory, byte[] bytes, String name, String desc, boolean slice)75 static TestSuite suiteForBytes( 76 ByteSourceFactory factory, byte[] bytes, String name, String desc, boolean slice) { 77 TestSuite suite = new TestSuite(name + " [" + desc + "]"); 78 for (Method method : testMethods) { 79 suite.addTest(new ByteSourceTester(factory, bytes, name, desc, method)); 80 } 81 82 if (slice && bytes.length > 0) { 83 // test a random slice() of the ByteSource 84 Random random = new Random(); 85 byte[] expected = factory.getExpected(bytes); 86 // if expected.length == 0, off has to be 0 but length doesn't matter--result will be empty 87 int off = expected.length == 0 ? 0 : random.nextInt(expected.length); 88 int len = expected.length == 0 ? 4 : random.nextInt(expected.length - off); 89 90 ByteSourceFactory sliced = SourceSinkFactories.asSlicedByteSourceFactory(factory, off, len); 91 suite.addTest(suiteForBytes(sliced, bytes, name + ".slice[long, long]", desc, false)); 92 93 // test a slice() of the ByteSource starting at a random offset with a length of 94 // Long.MAX_VALUE 95 ByteSourceFactory slicedLongMaxValue = 96 SourceSinkFactories.asSlicedByteSourceFactory(factory, off, Long.MAX_VALUE); 97 suite.addTest( 98 suiteForBytes( 99 slicedLongMaxValue, bytes, name + ".slice[long, Long.MAX_VALUE]", desc, false)); 100 101 // test a slice() of the ByteSource starting at an offset greater than its size 102 ByteSourceFactory slicedOffsetPastEnd = 103 SourceSinkFactories.asSlicedByteSourceFactory( 104 factory, expected.length + 2, expected.length + 10); 105 suite.addTest( 106 suiteForBytes(slicedOffsetPastEnd, bytes, name + ".slice[size + 2, long]", desc, false)); 107 } 108 109 return suite; 110 } 111 112 private ByteSource source; 113 ByteSourceTester( ByteSourceFactory factory, byte[] bytes, String suiteName, String caseDesc, Method method)114 public ByteSourceTester( 115 ByteSourceFactory factory, byte[] bytes, String suiteName, String caseDesc, Method method) { 116 super(factory, bytes, suiteName, caseDesc, method); 117 } 118 119 @Override setUp()120 public void setUp() throws IOException { 121 source = factory.createSource(data); 122 } 123 testOpenStream()124 public void testOpenStream() throws IOException { 125 InputStream in = source.openStream(); 126 try { 127 byte[] readBytes = ByteStreams.toByteArray(in); 128 assertExpectedBytes(readBytes); 129 } finally { 130 in.close(); 131 } 132 } 133 testOpenBufferedStream()134 public void testOpenBufferedStream() throws IOException { 135 InputStream in = source.openBufferedStream(); 136 try { 137 byte[] readBytes = ByteStreams.toByteArray(in); 138 assertExpectedBytes(readBytes); 139 } finally { 140 in.close(); 141 } 142 } 143 testRead()144 public void testRead() throws IOException { 145 byte[] readBytes = source.read(); 146 assertExpectedBytes(readBytes); 147 } 148 testCopyTo_outputStream()149 public void testCopyTo_outputStream() throws IOException { 150 ByteArrayOutputStream out = new ByteArrayOutputStream(); 151 source.copyTo(out); 152 assertExpectedBytes(out.toByteArray()); 153 } 154 testCopyTo_byteSink()155 public void testCopyTo_byteSink() throws IOException { 156 final ByteArrayOutputStream out = new ByteArrayOutputStream(); 157 // HERESY! but it's ok just for this I guess 158 source.copyTo( 159 new ByteSink() { 160 @Override 161 public OutputStream openStream() throws IOException { 162 return out; 163 } 164 }); 165 assertExpectedBytes(out.toByteArray()); 166 } 167 testIsEmpty()168 public void testIsEmpty() throws IOException { 169 assertEquals(expected.length == 0, source.isEmpty()); 170 } 171 testSize()172 public void testSize() throws IOException { 173 assertEquals(expected.length, source.size()); 174 } 175 testSizeIfKnown()176 public void testSizeIfKnown() throws IOException { 177 Optional<Long> sizeIfKnown = source.sizeIfKnown(); 178 if (sizeIfKnown.isPresent()) { 179 assertEquals(expected.length, (long) sizeIfKnown.get()); 180 } 181 } 182 testContentEquals()183 public void testContentEquals() throws IOException { 184 assertTrue( 185 source.contentEquals( 186 new ByteSource() { 187 @Override 188 public InputStream openStream() throws IOException { 189 return new RandomAmountInputStream( 190 new ByteArrayInputStream(expected), new Random()); 191 } 192 })); 193 } 194 testRead_usingByteProcessor()195 public void testRead_usingByteProcessor() throws IOException { 196 byte[] readBytes = 197 source.read( 198 new ByteProcessor<byte[]>() { 199 final ByteArrayOutputStream out = new ByteArrayOutputStream(); 200 201 @Override 202 public boolean processBytes(byte[] buf, int off, int len) throws IOException { 203 out.write(buf, off, len); 204 return true; 205 } 206 207 @Override 208 public byte[] getResult() { 209 return out.toByteArray(); 210 } 211 }); 212 213 assertExpectedBytes(readBytes); 214 } 215 testHash()216 public void testHash() throws IOException { 217 HashCode expectedHash = Hashing.md5().hashBytes(expected); 218 assertEquals(expectedHash, source.hash(Hashing.md5())); 219 } 220 testSlice_illegalArguments()221 public void testSlice_illegalArguments() { 222 try { 223 source.slice(-1, 0); 224 fail("expected IllegalArgumentException for call to slice with offset -1: " + source); 225 } catch (IllegalArgumentException expected) { 226 } 227 228 try { 229 source.slice(0, -1); 230 fail("expected IllegalArgumentException for call to slice with length -1: " + source); 231 } catch (IllegalArgumentException expected) { 232 } 233 } 234 235 // Test that you can not expand the readable data in a previously sliced ByteSource. testSlice_constrainedRange()236 public void testSlice_constrainedRange() throws IOException { 237 long size = source.read().length; 238 if (size >= 2) { 239 ByteSource sliced = source.slice(1, size - 2); 240 assertEquals(size - 2, sliced.read().length); 241 ByteSource resliced = sliced.slice(0, size - 1); 242 assertTrue(sliced.contentEquals(resliced)); 243 } 244 } 245 assertExpectedBytes(byte[] readBytes)246 private void assertExpectedBytes(byte[] readBytes) { 247 assertArrayEquals(expected, readBytes); 248 } 249 } 250