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