1/*
2 * Copyright (C) 2007 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
17package com.google.common.io;
18
19import static com.google.common.io.ByteStreams.copy;
20import static com.google.common.io.ByteStreams.newInputStreamSupplier;
21
22import com.google.common.base.Charsets;
23import com.google.common.base.Preconditions;
24import com.google.common.collect.ImmutableSet;
25import com.google.common.primitives.Bytes;
26import com.google.common.testing.TestLogHandler;
27
28import java.io.ByteArrayInputStream;
29import java.io.ByteArrayOutputStream;
30import java.io.EOFException;
31import java.io.FilterInputStream;
32import java.io.FilterOutputStream;
33import java.io.IOException;
34import java.io.InputStream;
35import java.io.OutputStream;
36import java.nio.channels.Channels;
37import java.nio.channels.ReadableByteChannel;
38import java.nio.channels.WritableByteChannel;
39import java.util.Arrays;
40import java.util.Random;
41
42/**
43 * Unit test for {@link ByteStreams}.
44 *
45 * @author Chris Nokleberg
46 */
47public class ByteStreamsTest extends IoTestCase {
48
49  /** Provides an InputStream that throws an IOException on every read. */
50  static final InputSupplier<InputStream> BROKEN_READ
51      = new InputSupplier<InputStream>() {
52        @Override
53        public InputStream getInput() {
54          return new InputStream() {
55            @Override public int read() throws IOException {
56              throw new IOException("broken read");
57            }
58          };
59        }
60      };
61
62  /** Provides an OutputStream that throws an IOException on every write. */
63  static final OutputSupplier<OutputStream> BROKEN_WRITE
64      = new OutputSupplier<OutputStream>() {
65        @Override
66        public OutputStream getOutput() {
67          return new OutputStream() {
68            @Override public void write(int b) throws IOException {
69              throw new IOException("broken write");
70            }
71          };
72        }
73      };
74
75  /** Provides an InputStream that throws an IOException on close. */
76  static final InputSupplier<InputStream> BROKEN_CLOSE_INPUT =
77      new InputSupplier<InputStream>() {
78        @Override
79        public InputStream getInput() {
80          return new FilterInputStream(new ByteArrayInputStream(new byte[10])) {
81            @Override public void close() throws IOException {
82              throw new IOException("broken close input");
83            }
84          };
85        }
86      };
87
88  /** Provides an OutputStream that throws an IOException on every close. */
89  static final OutputSupplier<OutputStream> BROKEN_CLOSE_OUTPUT =
90      new OutputSupplier<OutputStream>() {
91        @Override
92        public OutputStream getOutput() {
93          return new FilterOutputStream(new ByteArrayOutputStream()) {
94            @Override public void close() throws IOException {
95              throw new IOException("broken close output");
96            }
97          };
98        }
99      };
100
101  /** Throws an IOException from getInput. */
102  static final InputSupplier<InputStream> BROKEN_GET_INPUT =
103      new InputSupplier<InputStream>() {
104        @Override
105        public InputStream getInput() throws IOException {
106          throw new IOException("broken get input");
107        }
108      };
109
110  /** Throws an IOException from getOutput. */
111  static final OutputSupplier<OutputStream> BROKEN_GET_OUTPUT =
112      new OutputSupplier<OutputStream>() {
113        @Override
114        public OutputStream getOutput() throws IOException {
115          throw new IOException("broken get output");
116        }
117      };
118
119  private static final ImmutableSet<InputSupplier<InputStream>> BROKEN_INPUTS =
120      ImmutableSet.of(BROKEN_CLOSE_INPUT, BROKEN_GET_INPUT, BROKEN_READ);
121  private static final ImmutableSet<OutputSupplier<OutputStream>> BROKEN_OUTPUTS
122      = ImmutableSet.of(BROKEN_CLOSE_OUTPUT, BROKEN_GET_OUTPUT, BROKEN_WRITE);
123
124  public void testByteSuppliers() throws IOException {
125    byte[] range = newPreFilledByteArray(200);
126    assertTrue(Arrays.equals(range,
127        ByteStreams.toByteArray(ByteStreams.newInputStreamSupplier(range))));
128
129    byte[] subRange = ByteStreams.toByteArray(
130        ByteStreams.newInputStreamSupplier(range, 100, 50));
131    assertEquals(50, subRange.length);
132    assertEquals(100, subRange[0]);
133    assertEquals((byte) 149, subRange[subRange.length - 1]);
134  }
135
136  public void testEqual() throws IOException {
137    equalHelper(false, 0, 1);
138    equalHelper(false, 400, 10000);
139    equalHelper(false, 0x2000, 0x2001);
140    equalHelper(false, new byte[]{ 0 }, new byte[]{ 1 });
141
142    byte[] mutate = newPreFilledByteArray(10000);
143    mutate[9000] = 0;
144    equalHelper(false, mutate, newPreFilledByteArray(10000));
145
146    equalHelper(true, 0, 0);
147    equalHelper(true, 1, 1);
148    equalHelper(true, 400, 400);
149
150    final byte[] tenK = newPreFilledByteArray(10000);
151    equalHelper(true, tenK, tenK);
152    assertTrue(ByteStreams.equal(ByteStreams.newInputStreamSupplier(tenK),
153        new InputSupplier<InputStream>() {
154          @Override
155          public InputStream getInput() {
156            return new RandomAmountInputStream(new ByteArrayInputStream(tenK),
157                new Random(301));
158          }
159        }));
160  }
161
162  private void equalHelper(boolean expect, int size1, int size2)
163      throws IOException {
164    equalHelper(expect, newPreFilledByteArray(size1),
165        newPreFilledByteArray(size2));
166  }
167
168  private void equalHelper(boolean expect, byte[] a, byte[] b)
169      throws IOException {
170    assertEquals(expect, ByteStreams.equal(
171        ByteStreams.newInputStreamSupplier(a),
172        ByteStreams.newInputStreamSupplier(b)));
173  }
174
175  public void testAlwaysCloses() throws IOException {
176    byte[] range = newPreFilledByteArray(100);
177    CheckCloseSupplier.Input<InputStream> okRead
178        = newCheckInput(ByteStreams.newInputStreamSupplier(range));
179    CheckCloseSupplier.Output<OutputStream> okWrite
180        = newCheckOutput(new OutputSupplier<OutputStream>() {
181          @Override
182          public OutputStream getOutput() {
183            return new ByteArrayOutputStream();
184          }
185        });
186
187    CheckCloseSupplier.Input<InputStream> brokenRead
188        = newCheckInput(BROKEN_READ);
189    CheckCloseSupplier.Output<OutputStream> brokenWrite
190        = newCheckOutput(BROKEN_WRITE);
191
192    // copy, both suppliers
193    ByteStreams.copy(okRead, okWrite);
194    assertTrue(okRead.areClosed());
195    assertTrue(okWrite.areClosed());
196
197    try {
198      ByteStreams.copy(okRead, brokenWrite);
199      fail("expected exception");
200    } catch (IOException e) {
201      assertEquals("broken write", e.getMessage());
202    }
203    assertTrue(okRead.areClosed());
204    assertTrue(brokenWrite.areClosed());
205
206    try {
207      ByteStreams.copy(brokenRead, okWrite);
208      fail("expected exception");
209    } catch (IOException e) {
210      assertEquals("broken read", e.getMessage());
211    }
212    assertTrue(brokenRead.areClosed());
213    assertTrue(okWrite.areClosed());
214
215    try {
216      ByteStreams.copy(brokenRead, brokenWrite);
217      fail("expected exception");
218    } catch (IOException e) {
219      assertEquals("broken read", e.getMessage());
220    }
221    assertTrue(brokenRead.areClosed());
222    assertTrue(brokenWrite.areClosed());
223
224    // copy, input supplier
225    OutputStream out = okWrite.getOutput();
226    ByteStreams.copy(okRead, out);
227    assertTrue(okRead.areClosed());
228    assertFalse(okWrite.areClosed());
229    out.close();
230
231    out = brokenWrite.getOutput();
232    try {
233      ByteStreams.copy(okRead, out);
234      fail("expected exception");
235    } catch (IOException e) {
236      assertEquals("broken write", e.getMessage());
237    }
238    assertTrue(okRead.areClosed());
239    assertFalse(brokenWrite.areClosed());
240    out.close();
241
242    out = okWrite.getOutput();
243    try {
244      ByteStreams.copy(brokenRead, out);
245      fail("expected exception");
246    } catch (IOException e) {
247      assertEquals("broken read", e.getMessage());
248    }
249    assertTrue(brokenRead.areClosed());
250    assertFalse(okWrite.areClosed());
251    out.close();
252
253    out = brokenWrite.getOutput();
254    try {
255      ByteStreams.copy(brokenRead, out);
256      fail("expected exception");
257    } catch (IOException e) {
258      assertEquals("broken read", e.getMessage());
259    }
260    assertTrue(brokenRead.areClosed());
261    assertFalse(brokenWrite.areClosed());
262    out.close();
263
264    // copy, output supplier
265    InputStream in = okRead.getInput();
266    ByteStreams.copy(in, okWrite);
267    assertFalse(okRead.areClosed());
268    assertTrue(okWrite.areClosed());
269    in.close();
270
271    in = okRead.getInput();
272    try {
273      ByteStreams.copy(in, brokenWrite);
274      fail("expected exception");
275    } catch (IOException e) {
276      assertEquals("broken write", e.getMessage());
277    }
278    assertFalse(okRead.areClosed());
279    assertTrue(brokenWrite.areClosed());
280    in.close();
281
282    in = brokenRead.getInput();
283    try {
284      ByteStreams.copy(in, okWrite);
285      fail("expected exception");
286    } catch (IOException e) {
287      assertEquals("broken read", e.getMessage());
288    }
289    assertFalse(brokenRead.areClosed());
290    assertTrue(okWrite.areClosed());
291    in.close();
292
293    in = brokenRead.getInput();
294    try {
295      ByteStreams.copy(in, brokenWrite);
296      fail("expected exception");
297    } catch (IOException e) {
298      assertEquals("broken read", e.getMessage());
299    }
300    assertFalse(brokenRead.areClosed());
301    assertTrue(brokenWrite.areClosed());
302    in.close();
303
304    // toByteArray
305    assertTrue(Arrays.equals(range, ByteStreams.toByteArray(okRead)));
306    assertTrue(okRead.areClosed());
307
308    try {
309      ByteStreams.toByteArray(brokenRead);
310      fail("expected exception");
311    } catch (IOException e) {
312      assertEquals("broken read", e.getMessage());
313    }
314    assertTrue(brokenRead.areClosed());
315
316    // equal
317    try {
318      ByteStreams.equal(brokenRead, okRead);
319      fail("expected exception");
320    } catch (IOException e) {
321      assertEquals("broken read", e.getMessage());
322    }
323    assertTrue(brokenRead.areClosed());
324
325    try {
326      ByteStreams.equal(okRead, brokenRead);
327      fail("expected exception");
328    } catch (IOException e) {
329      assertEquals("broken read", e.getMessage());
330    }
331    assertTrue(brokenRead.areClosed());
332
333    // write
334    try {
335      ByteStreams.write(new byte[10], brokenWrite);
336      fail("expected exception");
337    } catch (IOException e) {
338      assertEquals("broken write", e.getMessage());
339    }
340    assertTrue(brokenWrite.areClosed());
341  }
342
343  private static int getAndResetRecords(TestLogHandler logHandler) {
344    int records = logHandler.getStoredLogRecords().size();
345    logHandler.clear();
346    return records;
347  }
348
349  private static void runFailureTest(
350      InputSupplier<? extends InputStream> in, OutputSupplier<OutputStream> out) {
351    try {
352      copy(in, out);
353      fail();
354    } catch (IOException expected) {
355    }
356  }
357
358  private static OutputSupplier<OutputStream> newByteArrayOutputStreamSupplier() {
359    return new OutputSupplier<OutputStream>() {
360      @Override public OutputStream getOutput() {
361        return new ByteArrayOutputStream();
362      }
363    };
364  }
365
366  public void testWriteBytes() throws IOException {
367    final ByteArrayOutputStream out = new ByteArrayOutputStream();
368    byte[] expected = newPreFilledByteArray(100);
369    ByteStreams.write(expected, new OutputSupplier<OutputStream>() {
370      @Override public OutputStream getOutput() {
371        return out;
372      }
373    });
374    assertTrue(Arrays.equals(expected, out.toByteArray()));
375  }
376
377  public void testCopy() throws Exception {
378    ByteArrayOutputStream out = new ByteArrayOutputStream();
379    byte[] expected = newPreFilledByteArray(100);
380    long num = ByteStreams.copy(new ByteArrayInputStream(expected), out);
381    assertEquals(100, num);
382    assertTrue(Arrays.equals(expected, out.toByteArray()));
383  }
384
385  public void testCopyChannel() throws IOException {
386    byte[] expected = newPreFilledByteArray(100);
387    ByteArrayOutputStream out = new ByteArrayOutputStream();
388    WritableByteChannel outChannel = Channels.newChannel(out);
389
390    ReadableByteChannel inChannel =
391        Channels.newChannel(new ByteArrayInputStream(expected));
392    ByteStreams.copy(inChannel, outChannel);
393    assertTrue(Arrays.equals(expected, out.toByteArray()));
394  }
395
396  public void testReadFully() throws IOException {
397    byte[] b = new byte[10];
398
399    try {
400      ByteStreams.readFully(newTestStream(10), null, 0, 10);
401      fail("expected exception");
402    } catch (NullPointerException e) {
403    }
404
405    try {
406      ByteStreams.readFully(null, b, 0, 10);
407      fail("expected exception");
408    } catch (NullPointerException e) {
409    }
410
411    try {
412      ByteStreams.readFully(newTestStream(10), b, -1, 10);
413      fail("expected exception");
414    } catch (IndexOutOfBoundsException e) {
415    }
416
417    try {
418      ByteStreams.readFully(newTestStream(10), b, 0, -1);
419      fail("expected exception");
420    } catch (IndexOutOfBoundsException e) {
421    }
422
423    try {
424      ByteStreams.readFully(newTestStream(10), b, 0, -1);
425      fail("expected exception");
426    } catch (IndexOutOfBoundsException e) {
427    }
428
429    try {
430      ByteStreams.readFully(newTestStream(10), b, 2, 10);
431      fail("expected exception");
432    } catch (IndexOutOfBoundsException e) {
433    }
434
435    try {
436      ByteStreams.readFully(newTestStream(5), b, 0, 10);
437      fail("expected exception");
438    } catch (EOFException e) {
439    }
440
441    Arrays.fill(b, (byte) 0);
442    ByteStreams.readFully(newTestStream(10), b, 0, 0);
443    assertTrue(Arrays.equals(new byte[10], b));
444
445    Arrays.fill(b, (byte) 0);
446    ByteStreams.readFully(newTestStream(10), b, 0, 10);
447    assertTrue(Arrays.equals(newPreFilledByteArray(10), b));
448
449    Arrays.fill(b, (byte) 0);
450    ByteStreams.readFully(newTestStream(10), b, 0, 5);
451    assertTrue(Arrays.equals(new byte[]{0, 1, 2, 3, 4, 0, 0, 0, 0, 0}, b));
452  }
453
454  public void testSkipFully() throws IOException {
455    byte[] bytes = newPreFilledByteArray(100);
456    skipHelper(0, 0, new ByteArrayInputStream(bytes));
457    skipHelper(50, 50, new ByteArrayInputStream(bytes));
458    skipHelper(50, 50, new SlowSkipper(new ByteArrayInputStream(bytes), 1));
459    skipHelper(50, 50, new SlowSkipper(new ByteArrayInputStream(bytes), 0));
460    skipHelper(100, -1, new ByteArrayInputStream(bytes));
461    try {
462      skipHelper(101, 0, new ByteArrayInputStream(bytes));
463      fail("expected exception");
464    } catch (EOFException e) {
465    }
466  }
467
468  private void skipHelper(long n, int expect, InputStream in)
469      throws IOException {
470    ByteStreams.skipFully(in, n);
471    assertEquals(expect, in.read());
472    in.close();
473  }
474
475  // TODO(user): rename; violates rule that only immutable things can be all caps
476  private static final byte[] BYTES = new byte[] {
477      0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10 };
478
479  public void testNewDataInput_empty() {
480    byte[] b = new byte[0];
481    ByteArrayDataInput in = ByteStreams.newDataInput(b);
482    try {
483      in.readInt();
484      fail();
485    } catch (IllegalStateException expected) {
486    }
487  }
488
489  public void testNewDataInput_normal() {
490    ByteArrayDataInput in = ByteStreams.newDataInput(BYTES);
491    assertEquals(0x12345678, in.readInt());
492    assertEquals(0x76543210, in.readInt());
493    try {
494      in.readInt();
495      fail();
496    } catch (IllegalStateException expected) {
497    }
498  }
499
500  public void testNewDataInput_readFully() {
501    ByteArrayDataInput in = ByteStreams.newDataInput(BYTES);
502    byte[] actual = new byte[BYTES.length];
503    in.readFully(actual);
504    assertEquals(BYTES, actual);
505  }
506
507  public void testNewDataInput_readFullyAndThenSome() {
508    ByteArrayDataInput in = ByteStreams.newDataInput(BYTES);
509    byte[] actual = new byte[BYTES.length * 2];
510    try {
511      in.readFully(actual);
512      fail();
513    } catch (IllegalStateException ex) {
514      assertTrue(ex.getCause() instanceof EOFException);
515    }
516  }
517
518  public void testNewDataInput_readFullyWithOffset() {
519    ByteArrayDataInput in = ByteStreams.newDataInput(BYTES);
520    byte[] actual = new byte[4];
521    in.readFully(actual, 2, 2);
522    assertEquals(0, actual[0]);
523    assertEquals(0, actual[1]);
524    assertEquals(BYTES[0], actual[2]);
525    assertEquals(BYTES[1], actual[3]);
526  }
527
528  public void testNewDataInput_readLine() {
529    ByteArrayDataInput in = ByteStreams.newDataInput(
530        "This is a line\r\nThis too\rand this\nand also this".getBytes(Charsets.UTF_8));
531    assertEquals("This is a line", in.readLine());
532    assertEquals("This too", in.readLine());
533    assertEquals("and this", in.readLine());
534    assertEquals("and also this", in.readLine());
535  }
536
537  public void testNewDataInput_readFloat() {
538    byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10};
539    ByteArrayDataInput in = ByteStreams.newDataInput(data);
540    assertEquals(Float.intBitsToFloat(0x12345678), in.readFloat(), 0.0);
541    assertEquals(Float.intBitsToFloat(0x76543210), in.readFloat(), 0.0);
542  }
543
544  public void testNewDataInput_readDouble() {
545    byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10};
546    ByteArrayDataInput in = ByteStreams.newDataInput(data);
547    assertEquals(Double.longBitsToDouble(0x1234567876543210L), in.readDouble(), 0.0);
548  }
549
550  public void testNewDataInput_readUTF() {
551    byte[] data = new byte[17];
552    data[1] = 15;
553    System.arraycopy("Kilroy was here".getBytes(Charsets.UTF_8), 0, data, 2, 15);
554    ByteArrayDataInput in = ByteStreams.newDataInput(data);
555    assertEquals("Kilroy was here", in.readUTF());
556  }
557
558  public void testNewDataInput_readChar() {
559    byte[] data = "qed".getBytes(Charsets.UTF_16BE);
560    ByteArrayDataInput in = ByteStreams.newDataInput(data);
561    assertEquals('q', in.readChar());
562    assertEquals('e', in.readChar());
563    assertEquals('d', in.readChar());
564  }
565
566  public void testNewDataInput_readUnsignedShort() {
567    byte[] data = {0, 0, 0, 1, (byte) 0xFF, (byte) 0xFF, 0x12, 0x34};
568    ByteArrayDataInput in = ByteStreams.newDataInput(data);
569    assertEquals(0, in.readUnsignedShort());
570    assertEquals(1, in.readUnsignedShort());
571    assertEquals(65535, in.readUnsignedShort());
572    assertEquals(0x1234, in.readUnsignedShort());
573  }
574
575  public void testNewDataInput_readLong() {
576    byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10};
577    ByteArrayDataInput in = ByteStreams.newDataInput(data);
578    assertEquals(0x1234567876543210L, in.readLong());
579  }
580
581  public void testNewDataInput_readBoolean() {
582    ByteArrayDataInput in = ByteStreams.newDataInput(BYTES);
583    assertTrue(in.readBoolean());
584  }
585
586  public void testNewDataInput_readByte() {
587    ByteArrayDataInput in = ByteStreams.newDataInput(BYTES);
588    for (int i = 0; i < BYTES.length; i++) {
589      assertEquals(BYTES[i], in.readByte());
590    }
591    try {
592      in.readByte();
593      fail();
594    } catch (IllegalStateException ex) {
595      assertTrue(ex.getCause() instanceof EOFException);
596    }
597  }
598
599  public void testNewDataInput_readUnsignedByte() {
600    ByteArrayDataInput in = ByteStreams.newDataInput(BYTES);
601    for (int i = 0; i < BYTES.length; i++) {
602      assertEquals(BYTES[i], in.readUnsignedByte());
603    }
604    try {
605      in.readUnsignedByte();
606      fail();
607    } catch (IllegalStateException ex) {
608      assertTrue(ex.getCause() instanceof EOFException);
609    }
610  }
611
612  public void testNewDataInput_offset() {
613    ByteArrayDataInput in = ByteStreams.newDataInput(BYTES, 2);
614    assertEquals(0x56787654, in.readInt());
615    try {
616      in.readInt();
617      fail();
618    } catch (IllegalStateException expected) {
619    }
620  }
621
622  public void testNewDataInput_skip() {
623    ByteArrayDataInput in = ByteStreams.newDataInput(new byte[2]);
624    in.skipBytes(2);
625    try {
626      in.skipBytes(1);
627    } catch (IllegalStateException expected) {
628    }
629  }
630
631  public void testNewDataOutput_empty() {
632    ByteArrayDataOutput out = ByteStreams.newDataOutput();
633    assertEquals(0, out.toByteArray().length);
634  }
635
636  public void testNewDataOutput_writeInt() {
637    ByteArrayDataOutput out = ByteStreams.newDataOutput();
638    out.writeInt(0x12345678);
639    out.writeInt(0x76543210);
640    assertTrue(Arrays.equals(BYTES, out.toByteArray()));
641  }
642
643  public void testNewDataOutput_sized() {
644    ByteArrayDataOutput out = ByteStreams.newDataOutput(4);
645    out.writeInt(0x12345678);
646    out.writeInt(0x76543210);
647    assertTrue(Arrays.equals(BYTES, out.toByteArray()));
648  }
649
650  public void testNewDataOutput_writeLong() {
651    ByteArrayDataOutput out = ByteStreams.newDataOutput();
652    out.writeLong(0x1234567876543210L);
653    assertTrue(Arrays.equals(BYTES, out.toByteArray()));
654  }
655
656  public void testNewDataOutput_writeByteArray() {
657    ByteArrayDataOutput out = ByteStreams.newDataOutput();
658    out.write(BYTES);
659    assertTrue(Arrays.equals(BYTES, out.toByteArray()));
660  }
661
662  public void testNewDataOutput_writeByte() {
663    ByteArrayDataOutput out = ByteStreams.newDataOutput();
664    out.write(0x12);
665    out.writeByte(0x34);
666    assertTrue(Arrays.equals(new byte[] {0x12, 0x34}, out.toByteArray()));
667  }
668
669  public void testNewDataOutput_writeByteOffset() {
670    ByteArrayDataOutput out = ByteStreams.newDataOutput();
671    out.write(BYTES, 4, 2);
672    byte[] expected = {BYTES[4], BYTES[5]};
673    assertEquals(expected, out.toByteArray());
674  }
675
676  public void testNewDataOutput_writeBoolean() {
677    ByteArrayDataOutput out = ByteStreams.newDataOutput();
678    out.writeBoolean(true);
679    out.writeBoolean(false);
680    byte[] expected = {(byte) 1, (byte) 0};
681    assertEquals(expected, out.toByteArray());
682  }
683
684  public void testNewDataOutput_writeChar() {
685    ByteArrayDataOutput out = ByteStreams.newDataOutput();
686    out.writeChar('a');
687    assertTrue(Arrays.equals(new byte[] {0, 97}, out.toByteArray()));
688  }
689
690  public void testNewDataOutput_writeChars() {
691    ByteArrayDataOutput out = ByteStreams.newDataOutput();
692    out.writeChars("r\u00C9sum\u00C9");
693    // need to remove byte order mark before comparing
694    byte[] expected = Arrays.copyOfRange("r\u00C9sum\u00C9".getBytes(Charsets.UTF_16), 2, 14);
695    assertEquals(expected, out.toByteArray());
696  }
697
698  public void testNewDataOutput_writeUTF() {
699    ByteArrayDataOutput out = ByteStreams.newDataOutput();
700    out.writeUTF("r\u00C9sum\u00C9");
701    byte[] expected ="r\u00C9sum\u00C9".getBytes(Charsets.UTF_8);
702    byte[] actual = out.toByteArray();
703    // writeUTF writes the length of the string in 2 bytes
704    assertEquals(0, actual[0]);
705    assertEquals(expected.length, actual[1]);
706    assertEquals(expected, Arrays.copyOfRange(actual, 2, actual.length));
707  }
708
709  public void testNewDataOutput_writeShort() {
710    ByteArrayDataOutput out = ByteStreams.newDataOutput();
711    out.writeShort(0x1234);
712    assertTrue(Arrays.equals(new byte[] {0x12, 0x34}, out.toByteArray()));
713  }
714
715  public void testNewDataOutput_writeDouble() {
716    ByteArrayDataOutput out = ByteStreams.newDataOutput();
717    out.writeDouble(Double.longBitsToDouble(0x1234567876543210L));
718    assertEquals(BYTES, out.toByteArray());
719  }
720
721  public void testNewDataOutput_writeFloat() {
722    ByteArrayDataOutput out = ByteStreams.newDataOutput();
723    out.writeFloat(Float.intBitsToFloat(0x12345678));
724    out.writeFloat(Float.intBitsToFloat(0x76543210));
725    assertEquals(BYTES, out.toByteArray());
726  }
727
728  public void testLength() throws IOException {
729    lengthHelper(Long.MAX_VALUE);
730    lengthHelper(7);
731    lengthHelper(1);
732    lengthHelper(0);
733
734    assertEquals(0, ByteStreams.length(
735        ByteStreams.newInputStreamSupplier(new byte[0])));
736  }
737
738  private void lengthHelper(final long skipLimit) throws IOException {
739    assertEquals(100, ByteStreams.length(new InputSupplier<InputStream>() {
740      @Override
741      public InputStream getInput() {
742        return new SlowSkipper(new ByteArrayInputStream(new byte[100]),
743            skipLimit);
744      }
745    }));
746  }
747
748  public void testSlice() throws IOException {
749    // Test preconditions
750    InputSupplier<? extends InputStream> supplier
751        = ByteStreams.newInputStreamSupplier(newPreFilledByteArray(100));
752    try {
753      ByteStreams.slice(supplier, -1, 10);
754      fail("expected exception");
755    } catch (IllegalArgumentException expected) {
756    }
757
758    try {
759      ByteStreams.slice(supplier, 0, -1);
760      fail("expected exception");
761    } catch (IllegalArgumentException expected) {
762    }
763
764    try {
765      ByteStreams.slice(null, 0, 10);
766      fail("expected exception");
767    } catch (NullPointerException expected) {
768    }
769
770    sliceHelper(0, 0, 0, 0);
771    sliceHelper(0, 0, 1, 0);
772    sliceHelper(100, 0, 10, 10);
773    sliceHelper(100, 0, 100, 100);
774    sliceHelper(100, 5, 10, 10);
775    sliceHelper(100, 5, 100, 95);
776    sliceHelper(100, 100, 0, 0);
777    sliceHelper(100, 100, 10, 0);
778
779    try {
780      sliceHelper(100, 101, 10, 0);
781      fail("expected exception");
782    } catch (EOFException expected) {
783    }
784  }
785
786  /**
787   * @param input the size of the input stream
788   * @param offset the first argument to {@link ByteStreams#slice}
789   * @param length the second argument to {@link ByteStreams#slice}
790   * @param expectRead the number of bytes we expect to read
791   */
792  private static void sliceHelper(
793      int input, int offset, long length, int expectRead) throws IOException {
794    Preconditions.checkArgument(expectRead == (int)
795        Math.max(0, Math.min(input, offset + length) - offset));
796    InputSupplier<? extends InputStream> supplier
797        = ByteStreams.newInputStreamSupplier(newPreFilledByteArray(input));
798    assertTrue(Arrays.equals(
799        newPreFilledByteArray(offset, expectRead),
800        ByteStreams.toByteArray(ByteStreams.slice(supplier, offset, length))));
801  }
802
803  private static InputStream newTestStream(int n) {
804    return new ByteArrayInputStream(newPreFilledByteArray(n));
805  }
806
807  private static CheckCloseSupplier.Input<InputStream> newCheckInput(
808      InputSupplier<? extends InputStream> delegate) {
809    return new CheckCloseSupplier.Input<InputStream>(delegate) {
810      @Override protected InputStream wrap(InputStream object,
811          final Callback callback) {
812        return new FilterInputStream(object) {
813          @Override public void close() throws IOException {
814            callback.delegateClosed();
815            super.close();
816          }
817        };
818      }
819    };
820  }
821
822  private static CheckCloseSupplier.Output<OutputStream> newCheckOutput(
823      OutputSupplier<? extends OutputStream> delegate) {
824    return new CheckCloseSupplier.Output<OutputStream>(delegate) {
825      @Override protected OutputStream wrap(OutputStream object,
826          final Callback callback) {
827        return new FilterOutputStream(object) {
828          @Override public void close() throws IOException {
829            callback.delegateClosed();
830            super.close();
831          }
832        };
833      }
834    };
835  }
836
837  /** Stream that will skip a maximum number of bytes at a time. */
838  private static class SlowSkipper extends FilterInputStream {
839    private final long max;
840
841    public SlowSkipper(InputStream in, long max) {
842      super(in);
843      this.max = max;
844    }
845
846    @Override public long skip(long n) throws IOException {
847      return super.skip(Math.min(max, n));
848    }
849  }
850
851  public void testByteProcessorStopEarly() throws IOException {
852    byte[] array = newPreFilledByteArray(6000);
853    assertEquals((Integer) 42,
854        ByteStreams.readBytes(ByteStreams.newInputStreamSupplier(array),
855            new ByteProcessor<Integer>() {
856              @Override
857              public boolean processBytes(byte[] buf, int off, int len) {
858                assertTrue(Arrays.equals(
859                    copyOfRange(buf, off, off + len),
860                    newPreFilledByteArray(4096)));
861                return false;
862              }
863
864              @Override
865              public Integer getResult() {
866                return 42;
867              }
868            }));
869  }
870
871  private static byte[] copyOfRange(byte[] in, int from, int to) {
872    byte[] out = new byte[to - from];
873    for (int i = 0; i < to - from; i++) {
874      out[i] = in[from + i];
875    }
876    return out;
877  }
878
879  private static void assertEquals(byte[] expected, byte[] actual) {
880    assertEquals(Bytes.asList(expected), Bytes.asList(actual));
881  }
882}
883