• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.base.Preconditions.checkArgument;
20 import static com.google.common.io.TestOption.AVAILABLE_ALWAYS_ZERO;
21 import static com.google.common.io.TestOption.CLOSE_THROWS;
22 import static com.google.common.io.TestOption.OPEN_THROWS;
23 import static com.google.common.io.TestOption.READ_THROWS;
24 import static com.google.common.io.TestOption.SKIP_THROWS;
25 import static com.google.common.io.TestOption.WRITE_THROWS;
26 import static org.junit.Assert.assertArrayEquals;
27 
28 import com.google.common.base.Charsets;
29 import com.google.common.collect.ImmutableList;
30 import com.google.common.collect.ImmutableSet;
31 import com.google.common.collect.Iterables;
32 import com.google.common.hash.Hashing;
33 import com.google.common.testing.TestLogHandler;
34 
35 import junit.framework.TestSuite;
36 
37 import java.io.ByteArrayOutputStream;
38 import java.io.EOFException;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.io.OutputStream;
42 import java.util.EnumSet;
43 
44 /**
45  * Tests for the default implementations of {@code ByteSource} methods.
46  *
47  * @author Colin Decker
48  */
49 public class ByteSourceTest extends IoTestCase {
50 
suite()51   public static TestSuite suite() {
52     TestSuite suite = new TestSuite();
53     suite.addTest(ByteSourceTester.tests("ByteSource.wrap[byte[]]",
54         SourceSinkFactories.byteArraySourceFactory(), true));
55     suite.addTest(ByteSourceTester.tests("ByteSource.empty[]",
56         SourceSinkFactories.emptyByteSourceFactory(), true));
57     suite.addTestSuite(ByteSourceTest.class);
58     return suite;
59   }
60 
61   private static final byte[] bytes = newPreFilledByteArray(10000);
62 
63   private TestByteSource source;
64 
65   @Override
setUp()66   protected void setUp() throws Exception {
67     source = new TestByteSource(bytes);
68   }
69 
testOpenBufferedStream()70   public void testOpenBufferedStream() throws IOException {
71     InputStream in = source.openBufferedStream();
72     assertTrue(source.wasStreamOpened());
73     assertFalse(source.wasStreamClosed());
74 
75     ByteArrayOutputStream out = new ByteArrayOutputStream();
76     ByteStreams.copy(in, out);
77     in.close();
78     out.close();
79 
80     assertTrue(source.wasStreamClosed());
81     assertArrayEquals(bytes, out.toByteArray());
82   }
83 
testSize()84   public void testSize() throws IOException {
85     assertEquals(bytes.length, source.size());
86     assertTrue(source.wasStreamOpened() && source.wasStreamClosed());
87 
88     // test that we can get the size even if skip() isn't supported
89     assertEquals(bytes.length, new TestByteSource(bytes, SKIP_THROWS).size());
90 
91     // test that we can get the size even if available() always returns zero
92     assertEquals(bytes.length, new TestByteSource(bytes, AVAILABLE_ALWAYS_ZERO).size());
93   }
94 
testCopyTo_outputStream()95   public void testCopyTo_outputStream() throws IOException {
96     ByteArrayOutputStream out = new ByteArrayOutputStream();
97 
98     assertEquals(bytes.length, source.copyTo(out));
99     assertTrue(source.wasStreamOpened() && source.wasStreamClosed());
100 
101     assertArrayEquals(bytes, out.toByteArray());
102   }
103 
testCopyTo_byteSink()104   public void testCopyTo_byteSink() throws IOException {
105     TestByteSink sink = new TestByteSink();
106 
107     assertFalse(sink.wasStreamOpened() || sink.wasStreamClosed());
108 
109     assertEquals(bytes.length, source.copyTo(sink));
110     assertTrue(source.wasStreamOpened() && source.wasStreamClosed());
111     assertTrue(sink.wasStreamOpened() && sink.wasStreamClosed());
112 
113     assertArrayEquals(bytes, sink.getBytes());
114   }
115 
testRead_toArray()116   public void testRead_toArray() throws IOException {
117     assertArrayEquals(bytes, source.read());
118     assertTrue(source.wasStreamOpened() && source.wasStreamClosed());
119   }
120 
testRead_withProcessor()121   public void testRead_withProcessor() throws IOException {
122     final byte[] processedBytes = new byte[bytes.length];
123     ByteProcessor<byte[]> processor = new ByteProcessor<byte[]>() {
124       int pos;
125 
126       @Override
127       public boolean processBytes(byte[] buf, int off, int len) throws IOException {
128         System.arraycopy(buf, off, processedBytes, pos, len);
129         pos += len;
130         return true;
131       }
132 
133       @Override
134       public byte[] getResult() {
135         return processedBytes;
136       }
137     };
138 
139     source.read(processor);
140     assertTrue(source.wasStreamOpened() && source.wasStreamClosed());
141 
142     assertArrayEquals(bytes, processedBytes);
143   }
144 
testRead_withProcessor_stopsOnFalse()145   public void testRead_withProcessor_stopsOnFalse() throws IOException {
146     ByteProcessor<Void> processor = new ByteProcessor<Void>() {
147       boolean firstCall = true;
148 
149       @Override
150       public boolean processBytes(byte[] buf, int off, int len) throws IOException {
151         assertTrue("consume() called twice", firstCall);
152         firstCall = false;
153         return false;
154       }
155 
156       @Override
157       public Void getResult() {
158         return null;
159       }
160     };
161 
162     source.read(processor);
163     assertTrue(source.wasStreamOpened() && source.wasStreamClosed());
164   }
165 
testHash()166   public void testHash() throws IOException {
167     ByteSource byteSource = new TestByteSource("hamburger\n".getBytes(Charsets.US_ASCII));
168 
169     // Pasted this expected string from `echo hamburger | md5sum`
170     assertEquals("cfa0c5002275c90508338a5cdb2a9781", byteSource.hash(Hashing.md5()).toString());
171   }
172 
testContentEquals()173   public void testContentEquals() throws IOException {
174     assertTrue(source.contentEquals(source));
175     assertTrue(source.wasStreamOpened() && source.wasStreamClosed());
176 
177     ByteSource equalSource = new TestByteSource(bytes);
178     assertTrue(source.contentEquals(equalSource));
179     assertTrue(new TestByteSource(bytes).contentEquals(source));
180 
181     ByteSource fewerBytes = new TestByteSource(newPreFilledByteArray(bytes.length / 2));
182     assertFalse(source.contentEquals(fewerBytes));
183 
184     byte[] copy = bytes.clone();
185     copy[9876] = 1;
186     ByteSource oneByteOff = new TestByteSource(copy);
187     assertFalse(source.contentEquals(oneByteOff));
188   }
189 
testSlice()190   public void testSlice() throws IOException {
191     // Test preconditions
192     try {
193       source.slice(-1, 10);
194       fail();
195     } catch (IllegalArgumentException expected) {
196     }
197 
198     try {
199       source.slice(0, -1);
200       fail();
201     } catch (IllegalArgumentException expected) {
202     }
203 
204     assertCorrectSlice(0, 0, 0, 0);
205     assertCorrectSlice(0, 0, 1, 0);
206     assertCorrectSlice(100, 0, 10, 10);
207     assertCorrectSlice(100, 0, 100, 100);
208     assertCorrectSlice(100, 5, 10, 10);
209     assertCorrectSlice(100, 5, 100, 95);
210     assertCorrectSlice(100, 100, 0, 0);
211     assertCorrectSlice(100, 100, 10, 0);
212 
213     try {
214       assertCorrectSlice(100, 101, 10, 0);
215       fail();
216     } catch (EOFException expected) {
217     }
218   }
219 
220   /**
221    * @param input      the size of the input source
222    * @param offset     the first argument to {@link ByteSource#slice}
223    * @param length     the second argument to {@link ByteSource#slice}
224    * @param expectRead the number of bytes we expect to read
225    */
assertCorrectSlice( int input, int offset, long length, int expectRead)226   private static void assertCorrectSlice(
227       int input, int offset, long length, int expectRead) throws IOException {
228     checkArgument(expectRead == (int) Math.max(0, Math.min(input, offset + length) - offset));
229 
230     byte[] expected = newPreFilledByteArray(offset, expectRead);
231 
232     ByteSource source = new TestByteSource(newPreFilledByteArray(input));
233     ByteSource slice = source.slice(offset, length);
234 
235     assertArrayEquals(expected, slice.read());
236   }
237 
testCopyToStream_doesNotCloseThatStream()238   public void testCopyToStream_doesNotCloseThatStream() throws IOException {
239     TestOutputStream out = new TestOutputStream(ByteStreams.nullOutputStream());
240     assertFalse(out.closed());
241     source.copyTo(out);
242     assertFalse(out.closed());
243   }
244 
testClosesOnErrors_copyingToByteSinkThatThrows()245   public void testClosesOnErrors_copyingToByteSinkThatThrows() {
246     for (TestOption option : EnumSet.of(OPEN_THROWS, WRITE_THROWS, CLOSE_THROWS)) {
247       TestByteSource okSource = new TestByteSource(bytes);
248       try {
249         okSource.copyTo(new TestByteSink(option));
250         fail();
251       } catch (IOException expected) {
252       }
253       // ensure stream was closed IF it was opened (depends on implementation whether or not it's
254       // opened at all if sink.newOutputStream() throws).
255       assertTrue("stream not closed when copying to sink with option: " + option,
256           !okSource.wasStreamOpened() || okSource.wasStreamClosed());
257     }
258   }
259 
testClosesOnErrors_whenReadThrows()260   public void testClosesOnErrors_whenReadThrows() {
261     TestByteSource failSource = new TestByteSource(bytes, READ_THROWS);
262     try {
263       failSource.copyTo(new TestByteSink());
264       fail();
265     } catch (IOException expected) {
266     }
267     assertTrue(failSource.wasStreamClosed());
268   }
269 
testClosesOnErrors_copyingToOutputStreamThatThrows()270   public void testClosesOnErrors_copyingToOutputStreamThatThrows() {
271     TestByteSource okSource = new TestByteSource(bytes);
272     try {
273       OutputStream out = new TestOutputStream(ByteStreams.nullOutputStream(), WRITE_THROWS);
274       okSource.copyTo(out);
275       fail();
276     } catch (IOException expected) {
277     }
278     assertTrue(okSource.wasStreamClosed());
279   }
280 
testConcat()281   public void testConcat() throws IOException {
282     ByteSource b1 = ByteSource.wrap(new byte[] {0, 1, 2, 3});
283     ByteSource b2 = ByteSource.wrap(new byte[0]);
284     ByteSource b3 = ByteSource.wrap(new byte[] {4, 5});
285 
286     byte[] expected = {0, 1, 2, 3, 4, 5};
287 
288     assertArrayEquals(expected,
289         ByteSource.concat(ImmutableList.of(b1, b2, b3)).read());
290     assertArrayEquals(expected,
291         ByteSource.concat(b1, b2, b3).read());
292     assertArrayEquals(expected,
293         ByteSource.concat(ImmutableList.of(b1, b2, b3).iterator()).read());
294     assertEquals(expected.length, ByteSource.concat(b1, b2, b3).size());
295     assertFalse(ByteSource.concat(b1, b2, b3).isEmpty());
296 
297     ByteSource emptyConcat = ByteSource.concat(ByteSource.empty(), ByteSource.empty());
298     assertTrue(emptyConcat.isEmpty());
299     assertEquals(0, emptyConcat.size());
300   }
301 
testConcat_infiniteIterable()302   public void testConcat_infiniteIterable() throws IOException {
303     ByteSource source = ByteSource.wrap(new byte[] {0, 1, 2, 3});
304     Iterable<ByteSource> cycle = Iterables.cycle(ImmutableList.of(source));
305     ByteSource concatenated = ByteSource.concat(cycle);
306 
307     byte[] expected = {0, 1, 2, 3, 0, 1, 2, 3};
308     assertArrayEquals(expected, concatenated.slice(0, 8).read());
309   }
310 
311   private static final ByteSource BROKEN_CLOSE_SOURCE
312       = new TestByteSource(new byte[10], CLOSE_THROWS);
313   private static final ByteSource BROKEN_OPEN_SOURCE
314       = new TestByteSource(new byte[10], OPEN_THROWS);
315   private static final ByteSource BROKEN_READ_SOURCE
316       = new TestByteSource(new byte[10], READ_THROWS);
317   private static final ByteSink BROKEN_CLOSE_SINK
318       = new TestByteSink(CLOSE_THROWS);
319   private static final ByteSink BROKEN_OPEN_SINK
320       = new TestByteSink(OPEN_THROWS);
321   private static final ByteSink BROKEN_WRITE_SINK
322       = new TestByteSink(WRITE_THROWS);
323 
324   private static final ImmutableSet<ByteSource> BROKEN_SOURCES
325       = ImmutableSet.of(BROKEN_CLOSE_SOURCE, BROKEN_OPEN_SOURCE, BROKEN_READ_SOURCE);
326   private static final ImmutableSet<ByteSink> BROKEN_SINKS
327       = ImmutableSet.of(BROKEN_CLOSE_SINK, BROKEN_OPEN_SINK, BROKEN_WRITE_SINK);
328 
testCopyExceptions()329   public void testCopyExceptions() {
330     if (!Closer.SuppressingSuppressor.isAvailable()) {
331       // test that exceptions are logged
332 
333       TestLogHandler logHandler = new TestLogHandler();
334       Closeables.logger.addHandler(logHandler);
335       try {
336         for (ByteSource in : BROKEN_SOURCES) {
337           runFailureTest(in, newNormalByteSink());
338           assertTrue(logHandler.getStoredLogRecords().isEmpty());
339 
340           runFailureTest(in, BROKEN_CLOSE_SINK);
341           assertEquals((in == BROKEN_OPEN_SOURCE) ? 0 : 1, getAndResetRecords(logHandler));
342         }
343 
344         for (ByteSink out : BROKEN_SINKS) {
345           runFailureTest(newNormalByteSource(), out);
346           assertTrue(logHandler.getStoredLogRecords().isEmpty());
347 
348           runFailureTest(BROKEN_CLOSE_SOURCE, out);
349           assertEquals(1, getAndResetRecords(logHandler));
350         }
351 
352         for (ByteSource in : BROKEN_SOURCES) {
353           for (ByteSink out : BROKEN_SINKS) {
354             runFailureTest(in, out);
355             assertTrue(getAndResetRecords(logHandler) <= 1);
356           }
357         }
358       } finally {
359         Closeables.logger.removeHandler(logHandler);
360       }
361     } else {
362       // test that exceptions are suppressed
363 
364       for (ByteSource in : BROKEN_SOURCES) {
365         int suppressed = runSuppressionFailureTest(in, newNormalByteSink());
366         assertEquals(0, suppressed);
367 
368         suppressed = runSuppressionFailureTest(in, BROKEN_CLOSE_SINK);
369         assertEquals((in == BROKEN_OPEN_SOURCE) ? 0 : 1, suppressed);
370       }
371 
372       for (ByteSink out : BROKEN_SINKS) {
373         int suppressed = runSuppressionFailureTest(newNormalByteSource(), out);
374         assertEquals(0, suppressed);
375 
376         suppressed = runSuppressionFailureTest(BROKEN_CLOSE_SOURCE, out);
377         assertEquals(1, suppressed);
378       }
379 
380       for (ByteSource in : BROKEN_SOURCES) {
381         for (ByteSink out : BROKEN_SINKS) {
382           int suppressed = runSuppressionFailureTest(in, out);
383           assertTrue(suppressed <= 1);
384         }
385       }
386     }
387   }
388 
getAndResetRecords(TestLogHandler logHandler)389   private static int getAndResetRecords(TestLogHandler logHandler) {
390     int records = logHandler.getStoredLogRecords().size();
391     logHandler.clear();
392     return records;
393   }
394 
runFailureTest(ByteSource in, ByteSink out)395   private static void runFailureTest(ByteSource in, ByteSink out) {
396     try {
397       in.copyTo(out);
398       fail();
399     } catch (IOException expected) {
400     }
401   }
402 
403   /**
404    * @return the number of exceptions that were suppressed on the expected thrown exception
405    */
runSuppressionFailureTest(ByteSource in, ByteSink out)406   private static int runSuppressionFailureTest(ByteSource in, ByteSink out) {
407     try {
408       in.copyTo(out);
409       fail();
410     } catch (IOException expected) {
411       return CloserTest.getSuppressed(expected).length;
412     }
413     throw new AssertionError(); // can't happen
414   }
415 
newNormalByteSource()416   private static ByteSource newNormalByteSource() {
417     return ByteSource.wrap(new byte[10]);
418   }
419 
newNormalByteSink()420   private static ByteSink newNormalByteSink() {
421     return new ByteSink() {
422       @Override public OutputStream openStream() {
423         return new ByteArrayOutputStream();
424       }
425     };
426   }
427 }
428