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 com.google.common.base.Charsets;
20
21import java.io.ByteArrayInputStream;
22import java.io.ByteArrayOutputStream;
23import java.io.EOFException;
24import java.io.FilterInputStream;
25import java.io.IOException;
26import java.io.InputStream;
27import java.io.OutputStream;
28import java.nio.channels.Channels;
29import java.nio.channels.ReadableByteChannel;
30import java.nio.channels.WritableByteChannel;
31import java.util.Arrays;
32
33/**
34 * Unit test for {@link ByteStreams}.
35 *
36 * @author Chris Nokleberg
37 */
38public class ByteStreamsTest extends IoTestCase {
39
40  public void testCopyChannel() throws IOException {
41    byte[] expected = newPreFilledByteArray(100);
42    ByteArrayOutputStream out = new ByteArrayOutputStream();
43    WritableByteChannel outChannel = Channels.newChannel(out);
44
45    ReadableByteChannel inChannel =
46        Channels.newChannel(new ByteArrayInputStream(expected));
47    ByteStreams.copy(inChannel, outChannel);
48    assertEquals(expected, out.toByteArray());
49  }
50
51  public void testReadFully() throws IOException {
52    byte[] b = new byte[10];
53
54    try {
55      ByteStreams.readFully(newTestStream(10), null, 0, 10);
56      fail("expected exception");
57    } catch (NullPointerException e) {
58    }
59
60    try {
61      ByteStreams.readFully(null, b, 0, 10);
62      fail("expected exception");
63    } catch (NullPointerException e) {
64    }
65
66    try {
67      ByteStreams.readFully(newTestStream(10), b, -1, 10);
68      fail("expected exception");
69    } catch (IndexOutOfBoundsException e) {
70    }
71
72    try {
73      ByteStreams.readFully(newTestStream(10), b, 0, -1);
74      fail("expected exception");
75    } catch (IndexOutOfBoundsException e) {
76    }
77
78    try {
79      ByteStreams.readFully(newTestStream(10), b, 0, -1);
80      fail("expected exception");
81    } catch (IndexOutOfBoundsException e) {
82    }
83
84    try {
85      ByteStreams.readFully(newTestStream(10), b, 2, 10);
86      fail("expected exception");
87    } catch (IndexOutOfBoundsException e) {
88    }
89
90    try {
91      ByteStreams.readFully(newTestStream(5), b, 0, 10);
92      fail("expected exception");
93    } catch (EOFException e) {
94    }
95
96    Arrays.fill(b, (byte) 0);
97    ByteStreams.readFully(newTestStream(10), b, 0, 0);
98    assertEquals(new byte[10], b);
99
100    Arrays.fill(b, (byte) 0);
101    ByteStreams.readFully(newTestStream(10), b, 0, 10);
102    assertEquals(newPreFilledByteArray(10), b);
103
104    Arrays.fill(b, (byte) 0);
105    ByteStreams.readFully(newTestStream(10), b, 0, 5);
106    assertEquals(new byte[]{0, 1, 2, 3, 4, 0, 0, 0, 0, 0}, b);
107  }
108
109  public void testSkipFully() throws IOException {
110    byte[] bytes = newPreFilledByteArray(100);
111    skipHelper(0, 0, new ByteArrayInputStream(bytes));
112    skipHelper(50, 50, new ByteArrayInputStream(bytes));
113    skipHelper(50, 50, new SlowSkipper(new ByteArrayInputStream(bytes), 1));
114    skipHelper(50, 50, new SlowSkipper(new ByteArrayInputStream(bytes), 0));
115    skipHelper(100, -1, new ByteArrayInputStream(bytes));
116    try {
117      skipHelper(101, 0, new ByteArrayInputStream(bytes));
118      fail("expected exception");
119    } catch (EOFException e) {
120    }
121  }
122
123  private static void skipHelper(long n, int expect, InputStream in)
124      throws IOException {
125    ByteStreams.skipFully(in, n);
126    assertEquals(expect, in.read());
127    in.close();
128  }
129
130  private static final byte[] bytes =
131      new byte[] { 0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10 };
132
133  public void testNewDataInput_empty() {
134    byte[] b = new byte[0];
135    ByteArrayDataInput in = ByteStreams.newDataInput(b);
136    try {
137      in.readInt();
138      fail("expected exception");
139    } catch (IllegalStateException expected) {
140    }
141  }
142
143  public void testNewDataInput_normal() {
144    ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
145    assertEquals(0x12345678, in.readInt());
146    assertEquals(0x76543210, in.readInt());
147    try {
148      in.readInt();
149      fail("expected exception");
150    } catch (IllegalStateException expected) {
151    }
152  }
153
154  public void testNewDataInput_readFully() {
155    ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
156    byte[] actual = new byte[bytes.length];
157    in.readFully(actual);
158    assertEquals(bytes, actual);
159  }
160
161  public void testNewDataInput_readFullyAndThenSome() {
162    ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
163    byte[] actual = new byte[bytes.length * 2];
164    try {
165      in.readFully(actual);
166      fail("expected exception");
167    } catch (IllegalStateException ex) {
168      assertTrue(ex.getCause() instanceof EOFException);
169    }
170  }
171
172  public void testNewDataInput_readFullyWithOffset() {
173    ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
174    byte[] actual = new byte[4];
175    in.readFully(actual, 2, 2);
176    assertEquals(0, actual[0]);
177    assertEquals(0, actual[1]);
178    assertEquals(bytes[0], actual[2]);
179    assertEquals(bytes[1], actual[3]);
180  }
181
182  public void testNewDataInput_readLine() {
183    ByteArrayDataInput in = ByteStreams.newDataInput(
184        "This is a line\r\nThis too\rand this\nand also this".getBytes(Charsets.UTF_8));
185    assertEquals("This is a line", in.readLine());
186    assertEquals("This too", in.readLine());
187    assertEquals("and this", in.readLine());
188    assertEquals("and also this", in.readLine());
189  }
190
191  public void testNewDataInput_readFloat() {
192    byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10};
193    ByteArrayDataInput in = ByteStreams.newDataInput(data);
194    assertEquals(Float.intBitsToFloat(0x12345678), in.readFloat(), 0.0);
195    assertEquals(Float.intBitsToFloat(0x76543210), in.readFloat(), 0.0);
196  }
197
198  public void testNewDataInput_readDouble() {
199    byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10};
200    ByteArrayDataInput in = ByteStreams.newDataInput(data);
201    assertEquals(Double.longBitsToDouble(0x1234567876543210L), in.readDouble(), 0.0);
202  }
203
204  public void testNewDataInput_readUTF() {
205    byte[] data = new byte[17];
206    data[1] = 15;
207    System.arraycopy("Kilroy was here".getBytes(Charsets.UTF_8), 0, data, 2, 15);
208    ByteArrayDataInput in = ByteStreams.newDataInput(data);
209    assertEquals("Kilroy was here", in.readUTF());
210  }
211
212  public void testNewDataInput_readChar() {
213    byte[] data = "qed".getBytes(Charsets.UTF_16BE);
214    ByteArrayDataInput in = ByteStreams.newDataInput(data);
215    assertEquals('q', in.readChar());
216    assertEquals('e', in.readChar());
217    assertEquals('d', in.readChar());
218  }
219
220  public void testNewDataInput_readUnsignedShort() {
221    byte[] data = {0, 0, 0, 1, (byte) 0xFF, (byte) 0xFF, 0x12, 0x34};
222    ByteArrayDataInput in = ByteStreams.newDataInput(data);
223    assertEquals(0, in.readUnsignedShort());
224    assertEquals(1, in.readUnsignedShort());
225    assertEquals(65535, in.readUnsignedShort());
226    assertEquals(0x1234, in.readUnsignedShort());
227  }
228
229  public void testNewDataInput_readLong() {
230    byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10};
231    ByteArrayDataInput in = ByteStreams.newDataInput(data);
232    assertEquals(0x1234567876543210L, in.readLong());
233  }
234
235  public void testNewDataInput_readBoolean() {
236    ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
237    assertTrue(in.readBoolean());
238  }
239
240  public void testNewDataInput_readByte() {
241    ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
242    for (int i = 0; i < bytes.length; i++) {
243      assertEquals(bytes[i], in.readByte());
244    }
245    try {
246      in.readByte();
247      fail("expected exception");
248    } catch (IllegalStateException ex) {
249      assertTrue(ex.getCause() instanceof EOFException);
250    }
251  }
252
253  public void testNewDataInput_readUnsignedByte() {
254    ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
255    for (int i = 0; i < bytes.length; i++) {
256      assertEquals(bytes[i], in.readUnsignedByte());
257    }
258    try {
259      in.readUnsignedByte();
260      fail("expected exception");
261    } catch (IllegalStateException ex) {
262      assertTrue(ex.getCause() instanceof EOFException);
263    }
264  }
265
266  public void testNewDataInput_offset() {
267    ByteArrayDataInput in = ByteStreams.newDataInput(bytes, 2);
268    assertEquals(0x56787654, in.readInt());
269    try {
270      in.readInt();
271      fail("expected exception");
272    } catch (IllegalStateException expected) {
273    }
274  }
275
276  public void testNewDataInput_skip() {
277    ByteArrayDataInput in = ByteStreams.newDataInput(new byte[2]);
278    assertEquals(2, in.skipBytes(2));
279    assertEquals(0, in.skipBytes(1));
280  }
281
282  public void testNewDataInput_BAIS() {
283    ByteArrayInputStream bais = new ByteArrayInputStream(new byte[] {0x12, 0x34, 0x56, 0x78});
284    ByteArrayDataInput in = ByteStreams.newDataInput(bais);
285    assertEquals(0x12345678, in.readInt());
286  }
287
288  public void testNewDataOutput_empty() {
289    ByteArrayDataOutput out = ByteStreams.newDataOutput();
290    assertEquals(0, out.toByteArray().length);
291  }
292
293  public void testNewDataOutput_writeInt() {
294    ByteArrayDataOutput out = ByteStreams.newDataOutput();
295    out.writeInt(0x12345678);
296    out.writeInt(0x76543210);
297    assertEquals(bytes, out.toByteArray());
298  }
299
300  public void testNewDataOutput_sized() {
301    ByteArrayDataOutput out = ByteStreams.newDataOutput(4);
302    out.writeInt(0x12345678);
303    out.writeInt(0x76543210);
304    assertEquals(bytes, out.toByteArray());
305  }
306
307  public void testNewDataOutput_writeLong() {
308    ByteArrayDataOutput out = ByteStreams.newDataOutput();
309    out.writeLong(0x1234567876543210L);
310    assertEquals(bytes, out.toByteArray());
311  }
312
313  public void testNewDataOutput_writeByteArray() {
314    ByteArrayDataOutput out = ByteStreams.newDataOutput();
315    out.write(bytes);
316    assertEquals(bytes, out.toByteArray());
317  }
318
319  public void testNewDataOutput_writeByte() {
320    ByteArrayDataOutput out = ByteStreams.newDataOutput();
321    out.write(0x12);
322    out.writeByte(0x34);
323    assertEquals(new byte[] {0x12, 0x34}, out.toByteArray());
324  }
325
326  public void testNewDataOutput_writeByteOffset() {
327    ByteArrayDataOutput out = ByteStreams.newDataOutput();
328    out.write(bytes, 4, 2);
329    byte[] expected = {bytes[4], bytes[5]};
330    assertEquals(expected, out.toByteArray());
331  }
332
333  public void testNewDataOutput_writeBoolean() {
334    ByteArrayDataOutput out = ByteStreams.newDataOutput();
335    out.writeBoolean(true);
336    out.writeBoolean(false);
337    byte[] expected = {(byte) 1, (byte) 0};
338    assertEquals(expected, out.toByteArray());
339  }
340
341  public void testNewDataOutput_writeChar() {
342    ByteArrayDataOutput out = ByteStreams.newDataOutput();
343    out.writeChar('a');
344    assertEquals(new byte[] {0, 97}, out.toByteArray());
345  }
346
347  public void testNewDataOutput_writeChars() {
348    ByteArrayDataOutput out = ByteStreams.newDataOutput();
349    out.writeChars("r\u00C9sum\u00C9");
350    // need to remove byte order mark before comparing
351    byte[] expected = Arrays.copyOfRange("r\u00C9sum\u00C9".getBytes(Charsets.UTF_16), 2, 14);
352    assertEquals(expected, out.toByteArray());
353  }
354
355  public void testNewDataOutput_writeUTF() {
356    ByteArrayDataOutput out = ByteStreams.newDataOutput();
357    out.writeUTF("r\u00C9sum\u00C9");
358    byte[] expected ="r\u00C9sum\u00C9".getBytes(Charsets.UTF_8);
359    byte[] actual = out.toByteArray();
360    // writeUTF writes the length of the string in 2 bytes
361    assertEquals(0, actual[0]);
362    assertEquals(expected.length, actual[1]);
363    assertEquals(expected, Arrays.copyOfRange(actual, 2, actual.length));
364  }
365
366  public void testNewDataOutput_writeShort() {
367    ByteArrayDataOutput out = ByteStreams.newDataOutput();
368    out.writeShort(0x1234);
369    assertEquals(new byte[] {0x12, 0x34}, out.toByteArray());
370  }
371
372  public void testNewDataOutput_writeDouble() {
373    ByteArrayDataOutput out = ByteStreams.newDataOutput();
374    out.writeDouble(Double.longBitsToDouble(0x1234567876543210L));
375    assertEquals(bytes, out.toByteArray());
376  }
377
378  public void testNewDataOutput_writeFloat() {
379    ByteArrayDataOutput out = ByteStreams.newDataOutput();
380    out.writeFloat(Float.intBitsToFloat(0x12345678));
381    out.writeFloat(Float.intBitsToFloat(0x76543210));
382    assertEquals(bytes, out.toByteArray());
383  }
384
385  public void testNewDataOutput_BAOS() {
386    ByteArrayOutputStream baos = new ByteArrayOutputStream();
387    ByteArrayDataOutput out = ByteStreams.newDataOutput(baos);
388    out.writeInt(0x12345678);
389    assertEquals(4, baos.size());
390    assertEquals(new byte[] {0x12, 0x34, 0x56, 0x78}, baos.toByteArray());
391  }
392
393  public void testToByteArray_withSize_givenCorrectSize() throws IOException {
394    InputStream in = newTestStream(100);
395    byte[] b = ByteStreams.toByteArray(in, 100);
396    assertEquals(100, b.length);
397  }
398
399  public void testToByteArray_withSize_givenSmallerSize() throws IOException {
400    InputStream in = newTestStream(100);
401    byte[] b = ByteStreams.toByteArray(in, 80);
402    assertEquals(100, b.length);
403  }
404
405  public void testToByteArray_withSize_givenLargerSize() throws IOException {
406    InputStream in = newTestStream(100);
407    byte[] b = ByteStreams.toByteArray(in, 120);
408    assertEquals(100, b.length);
409  }
410
411  public void testToByteArray_withSize_givenSizeZero() throws IOException {
412    InputStream in = newTestStream(100);
413    byte[] b = ByteStreams.toByteArray(in, 0);
414    assertEquals(100, b.length);
415  }
416
417  private static InputStream newTestStream(int n) {
418    return new ByteArrayInputStream(newPreFilledByteArray(n));
419  }
420
421  /** Stream that will skip a maximum number of bytes at a time. */
422  private static class SlowSkipper extends FilterInputStream {
423    private final long max;
424
425    public SlowSkipper(InputStream in, long max) {
426      super(in);
427      this.max = max;
428    }
429
430    @Override public long skip(long n) throws IOException {
431      return super.skip(Math.min(max, n));
432    }
433  }
434
435  public void testReadBytes() throws IOException {
436    final byte[] array = newPreFilledByteArray(1000);
437    assertEquals(array, ByteStreams.readBytes(
438      new ByteArrayInputStream(array), new TestByteProcessor()));
439  }
440
441  private class TestByteProcessor implements ByteProcessor<byte[]> {
442    private final ByteArrayOutputStream out = new ByteArrayOutputStream();
443
444    @Override
445    public boolean processBytes(byte[] buf, int off, int len)
446        throws IOException {
447      out.write(buf, off, len);
448      return true;
449    }
450
451    @Override
452    public byte[] getResult() {
453      return out.toByteArray();
454    }
455  }
456
457  public void testByteProcessorStopEarly() throws IOException {
458    byte[] array = newPreFilledByteArray(6000);
459    assertEquals((Integer) 42,
460        ByteStreams.readBytes(new ByteArrayInputStream(array),
461            new ByteProcessor<Integer>() {
462              @Override
463              public boolean processBytes(byte[] buf, int off, int len) {
464                assertEquals(
465                    copyOfRange(buf, off, off + len),
466                    newPreFilledByteArray(4096));
467                return false;
468              }
469
470              @Override
471              public Integer getResult() {
472                return 42;
473              }
474            }));
475  }
476
477  public void testNullOutputStream() throws Exception {
478    // create a null output stream
479    OutputStream nos = ByteStreams.nullOutputStream();
480    // write to the output stream
481    nos.write('n');
482    String test = "Test string for NullOutputStream";
483    nos.write(test.getBytes());
484    nos.write(test.getBytes(), 2, 10);
485    // nothing really to assert?
486    assertSame(ByteStreams.nullOutputStream(), ByteStreams.nullOutputStream());
487  }
488
489  public void testLimit() throws Exception {
490    byte[] big = newPreFilledByteArray(5);
491    InputStream bin = new ByteArrayInputStream(big);
492    InputStream lin = ByteStreams.limit(bin, 2);
493
494    // also test available
495    lin.mark(2);
496    assertEquals(2, lin.available());
497    int read = lin.read();
498    assertEquals(big[0], read);
499    assertEquals(1, lin.available());
500    read = lin.read();
501    assertEquals(big[1], read);
502    assertEquals(0, lin.available());
503    read = lin.read();
504    assertEquals(-1, read);
505
506    lin.reset();
507    byte[] small = new byte[5];
508    read = lin.read(small);
509    assertEquals(2, read);
510    assertEquals(big[0], small[0]);
511    assertEquals(big[1], small[1]);
512
513    lin.reset();
514    read = lin.read(small, 2, 3);
515    assertEquals(2, read);
516    assertEquals(big[0], small[2]);
517    assertEquals(big[1], small[3]);
518  }
519
520  public void testLimit_mark() throws Exception {
521    byte[] big = newPreFilledByteArray(5);
522    InputStream bin = new ByteArrayInputStream(big);
523    InputStream lin = ByteStreams.limit(bin, 2);
524
525    int read = lin.read();
526    assertEquals(big[0], read);
527    lin.mark(2);
528
529    read = lin.read();
530    assertEquals(big[1], read);
531    read = lin.read();
532    assertEquals(-1, read);
533
534    lin.reset();
535    read = lin.read();
536    assertEquals(big[1], read);
537    read = lin.read();
538    assertEquals(-1, read);
539  }
540
541  public void testLimit_skip() throws Exception {
542    byte[] big = newPreFilledByteArray(5);
543    InputStream bin = new ByteArrayInputStream(big);
544    InputStream lin = ByteStreams.limit(bin, 2);
545
546    // also test available
547    lin.mark(2);
548    assertEquals(2, lin.available());
549    lin.skip(1);
550    assertEquals(1, lin.available());
551
552    lin.reset();
553    assertEquals(2, lin.available());
554    lin.skip(3);
555    assertEquals(0, lin.available());
556  }
557
558  public void testLimit_markNotSet() {
559    byte[] big = newPreFilledByteArray(5);
560    InputStream bin = new ByteArrayInputStream(big);
561    InputStream lin = ByteStreams.limit(bin, 2);
562
563    try {
564      lin.reset();
565      fail();
566    } catch (IOException expected) {
567      assertEquals("Mark not set", expected.getMessage());
568    }
569  }
570
571  public void testLimit_markNotSupported() {
572    InputStream lin = ByteStreams.limit(new UnmarkableInputStream(), 2);
573
574    try {
575      lin.reset();
576      fail();
577    } catch (IOException expected) {
578      assertEquals("Mark not supported", expected.getMessage());
579    }
580  }
581
582  private static class UnmarkableInputStream extends InputStream {
583    @Override
584    public int read() throws IOException {
585      return 0;
586    }
587
588    @Override
589    public boolean markSupported() {
590      return false;
591    }
592  }
593
594  private static byte[] copyOfRange(byte[] in, int from, int to) {
595    byte[] out = new byte[to - from];
596    for (int i = 0; i < to - from; i++) {
597      out[i] = in[from + i];
598    }
599    return out;
600  }
601
602  private static void assertEquals(byte[] expected, byte[] actual) {
603    assertTrue(Arrays.equals(expected, actual));
604  }
605}
606