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.base.Charsets.UTF_8;
20import static com.google.common.io.CharStreams.copy;
21import static com.google.common.io.CharStreams.newReaderSupplier;
22
23import com.google.common.base.Charsets;
24import com.google.common.collect.ImmutableList;
25import com.google.common.collect.ImmutableSet;
26import com.google.common.testing.TestLogHandler;
27
28import java.io.ByteArrayInputStream;
29import java.io.EOFException;
30import java.io.FilterReader;
31import java.io.FilterWriter;
32import java.io.IOException;
33import java.io.InputStreamReader;
34import java.io.Reader;
35import java.io.StringReader;
36import java.io.StringWriter;
37import java.io.Writer;
38import java.util.List;
39
40/**
41 * Unit test for {@link CharStreams}.
42 *
43 * @author Chris Nokleberg
44 */
45public class CharStreamsTest extends IoTestCase {
46  private static final String TEXT
47      = "The quick brown fox jumped over the lazy dog.";
48
49  static final InputSupplier<? extends Reader> BROKEN_READ
50      = CharStreams.newReaderSupplier(ByteStreamsTest.BROKEN_READ, UTF_8);
51
52  static final OutputSupplier<? extends Writer> BROKEN_WRITE
53      = CharStreams.newWriterSupplier(ByteStreamsTest.BROKEN_WRITE, UTF_8);
54
55  static final InputSupplier<? extends Reader> BROKEN_CLOSE_INPUT
56      = CharStreams.newReaderSupplier(ByteStreamsTest.BROKEN_CLOSE_INPUT, UTF_8);
57
58  static final OutputSupplier<? extends Writer> BROKEN_CLOSE_OUTPUT
59      = CharStreams.newWriterSupplier(ByteStreamsTest.BROKEN_CLOSE_OUTPUT, UTF_8);
60
61  static final InputSupplier<? extends Reader> BROKEN_GET_INPUT
62      = CharStreams.newReaderSupplier(ByteStreamsTest.BROKEN_GET_INPUT, UTF_8);
63
64  static final OutputSupplier<? extends Writer> BROKEN_GET_OUTPUT
65      = CharStreams.newWriterSupplier(ByteStreamsTest.BROKEN_GET_OUTPUT, UTF_8);
66
67  private static final ImmutableSet<InputSupplier<? extends Reader>> BROKEN_INPUTS =
68      ImmutableSet.of(BROKEN_CLOSE_INPUT, BROKEN_GET_INPUT, BROKEN_READ);
69  private static final ImmutableSet<OutputSupplier<? extends Writer>> BROKEN_OUTPUTS
70      = ImmutableSet.of(BROKEN_CLOSE_OUTPUT, BROKEN_GET_OUTPUT, BROKEN_WRITE);
71
72  public void testToString() throws IOException {
73    assertEquals(TEXT, CharStreams.toString(new StringReader(TEXT)));
74    assertEquals(TEXT,
75        CharStreams.toString(CharStreams.newReaderSupplier(TEXT)));
76  }
77
78  public void testSkipFully_blockingRead() throws IOException {
79    Reader reader = new NonSkippingReader("abcdef");
80    CharStreams.skipFully(reader, 6);
81    assertEquals(-1, reader.read());
82  }
83
84  private static class NonSkippingReader extends StringReader {
85    NonSkippingReader(String s) {
86      super(s);
87    }
88
89    @Override
90    public long skip(long n) {
91      return 0;
92    }
93  }
94
95  public void testReadLines_fromReadable() throws IOException {
96    byte[] bytes = "a\nb\nc".getBytes(Charsets.UTF_8.name());
97    List<String> lines = CharStreams.readLines(
98        new InputStreamReader(new ByteArrayInputStream(bytes), Charsets.UTF_8));
99    assertEquals(ImmutableList.of("a", "b", "c"), lines);
100  }
101
102  public void testReadLines_withLineProcessor() throws IOException {
103    InputSupplier<StringReader> r = CharStreams.newReaderSupplier("a\nb\nc");
104
105    // Test a LineProcessor that always returns false.
106    LineProcessor<Integer> alwaysFalse = new LineProcessor<Integer>() {
107      int seen;
108      @Override
109      public boolean processLine(String line) {
110        seen++;
111        return false;
112      }
113      @Override
114      public Integer getResult() {
115        return seen;
116      }
117    };
118    assertEquals("processLine was called more than once", 1,
119        CharStreams.readLines(r, alwaysFalse).intValue());
120
121    // Test a LineProcessor that always returns true.
122    LineProcessor<Integer> alwaysTrue = new LineProcessor<Integer>() {
123      int seen;
124      @Override
125      public boolean processLine(String line) {
126        seen++;
127        return true;
128      }
129      @Override
130      public Integer getResult() {
131        return seen;
132      }
133    };
134    assertEquals("processLine was not called for all the lines", 3,
135        CharStreams.readLines(r, alwaysTrue).intValue());
136
137    // Test a LineProcessor that is conditional.
138    final StringBuilder sb = new StringBuilder();
139    LineProcessor<Integer> conditional = new LineProcessor<Integer>() {
140      int seen;
141      @Override
142      public boolean processLine(String line) {
143        seen++;
144        sb.append(line);
145        return seen < 2;
146      }
147      @Override
148      public Integer getResult() {
149        return seen;
150      }
151    };
152    assertEquals(2, CharStreams.readLines(r, conditional).intValue());
153    assertEquals("ab", sb.toString());
154  }
155
156  public void testAlwaysCloses() throws IOException {
157    CheckCloseSupplier.Input<Reader> okRead
158        = newCheckReader(CharStreams.newReaderSupplier(TEXT));
159    CheckCloseSupplier.Output<Writer> okWrite
160        = newCheckWriter(new OutputSupplier<Writer>() {
161          @Override
162          public Writer getOutput() {
163            return new StringWriter();
164          }
165        });
166    CheckCloseSupplier.Input<Reader> brokenRead = newCheckReader(BROKEN_READ);
167    CheckCloseSupplier.Output<Writer> brokenWrite
168        = newCheckWriter(BROKEN_WRITE);
169
170    CharStreams.copy(okRead, okWrite);
171    assertTrue(okRead.areClosed());
172    assertTrue(okWrite.areClosed());
173
174    try {
175      CharStreams.copy(okRead, brokenWrite);
176      fail("expected exception");
177    } catch (Exception e) {
178      assertEquals("broken write", e.getMessage());
179    }
180    assertTrue(okRead.areClosed());
181    assertTrue(brokenWrite.areClosed());
182
183    try {
184      CharStreams.copy(brokenRead, okWrite);
185      fail("expected exception");
186    } catch (Exception e) {
187      assertEquals("broken read", e.getMessage());
188    }
189    assertTrue(brokenRead.areClosed());
190    assertTrue(okWrite.areClosed());
191
192    try {
193      CharStreams.copy(brokenRead, brokenWrite);
194      fail("expected exception");
195    } catch (Exception e) {
196      assertEquals("broken read", e.getMessage());
197    }
198    assertTrue(brokenRead.areClosed());
199    assertTrue(brokenWrite.areClosed());
200
201    assertEquals(TEXT, CharStreams.toString(okRead));
202    assertTrue(okRead.areClosed());
203
204    try {
205      CharStreams.toString(brokenRead);
206      fail("expected exception");
207    } catch (Exception e) {
208      assertEquals("broken read", e.getMessage());
209    }
210    assertTrue(brokenRead.areClosed());
211
212    try {
213      CharStreams.write("hello world", brokenWrite);
214      fail("expected exception");
215    } catch (Exception e) {
216      assertEquals("broken write", e.getMessage());
217    }
218    assertTrue(brokenWrite.areClosed());
219  }
220
221  private static int getAndResetRecords(TestLogHandler logHandler) {
222    int records = logHandler.getStoredLogRecords().size();
223    logHandler.clear();
224    return records;
225  }
226
227  private static void runFailureTest(
228      InputSupplier<? extends Reader> in, OutputSupplier<? extends Writer> out) {
229    try {
230      copy(in, out);
231      fail();
232    } catch (IOException expected) {
233    }
234  }
235
236  private static OutputSupplier<Writer> newStringWriterSupplier() {
237    return new OutputSupplier<Writer>() {
238      @Override public Writer getOutput() {
239        return new StringWriter();
240      }
241    };
242  }
243
244  public void testSkipFully_EOF() throws IOException {
245    Reader reader = new StringReader("abcde");
246    try {
247      CharStreams.skipFully(reader, 6);
248      fail("expected EOFException");
249    } catch (EOFException e) {
250      // expected
251    }
252  }
253
254  public void testSkipFully() throws IOException {
255    String testString = "abcdef";
256    Reader reader = new StringReader(testString);
257
258    assertEquals(testString.charAt(0), reader.read());
259    CharStreams.skipFully(reader, 1);
260    assertEquals(testString.charAt(2), reader.read());
261    CharStreams.skipFully(reader, 2);
262    assertEquals(testString.charAt(5), reader.read());
263
264    assertEquals(-1, reader.read());
265  }
266
267  public void testAsWriter() {
268    // Should wrap Appendable in a new object
269    Appendable plainAppendable = new StringBuilder();
270    Writer result = CharStreams.asWriter(plainAppendable);
271    assertNotSame(plainAppendable, result);
272    assertNotNull(result);
273
274    // A Writer should not be wrapped
275    Appendable secretlyAWriter = new StringWriter();
276    result = CharStreams.asWriter(secretlyAWriter);
277    assertSame(secretlyAWriter, result);
278  }
279
280  public void testWriteString() throws IOException {
281    final StringWriter sw = new StringWriter();
282    String expected = "foo";
283    CharStreams.write(expected, new OutputSupplier<Writer>() {
284      @Override public Writer getOutput() {
285        return sw;
286      }
287    });
288    assertEquals(expected, sw.toString());
289  }
290
291  private static CheckCloseSupplier.Input<Reader> newCheckReader(
292      InputSupplier<? extends Reader> delegate) {
293    return new CheckCloseSupplier.Input<Reader>(delegate) {
294      @Override protected Reader wrap(Reader object, final Callback callback) {
295        return new FilterReader(object) {
296          @Override public void close() throws IOException {
297            callback.delegateClosed();
298            super.close();
299          }
300        };
301      }
302    };
303  }
304
305  private static CheckCloseSupplier.Output<Writer> newCheckWriter(
306      OutputSupplier<? extends Writer> delegate) {
307    return new CheckCloseSupplier.Output<Writer>(delegate) {
308      @Override protected Writer wrap(Writer object, final Callback callback) {
309        return new FilterWriter(object) {
310          @Override public void close() throws IOException {
311            callback.delegateClosed();
312            super.close();
313          }
314        };
315      }
316    };
317  }
318}
319