Home | History | Annotate | Download | only in io
      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 
     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
     66   protected void setUp() throws Exception {
     67     source = new TestByteSource(bytes);
     68   }
     69 
     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 
     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 
     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 
    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 
    116   public void testRead_toArray() throws IOException {
    117     assertArrayEquals(bytes, source.read());
    118     assertTrue(source.wasStreamOpened() && source.wasStreamClosed());
    119   }
    120 
    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 
    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 
    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 
    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 
    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    */
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    389   private static int getAndResetRecords(TestLogHandler logHandler) {
    390     int records = logHandler.getStoredLogRecords().size();
    391     logHandler.clear();
    392     return records;
    393   }
    394 
    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    */
    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 
    416   private static ByteSource newNormalByteSource() {
    417     return ByteSource.wrap(new byte[10]);
    418   }
    419 
    420   private static ByteSink newNormalByteSink() {
    421     return new ByteSink() {
    422       @Override public OutputStream openStream() {
    423         return new ByteArrayOutputStream();
    424       }
    425     };
    426   }
    427 }
    428