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.Strings;
20import com.google.common.collect.ImmutableList;
21
22import java.io.EOFException;
23import java.io.FilterReader;
24import java.io.IOException;
25import java.io.Reader;
26import java.io.StringReader;
27import java.io.StringWriter;
28import java.io.Writer;
29import java.util.List;
30
31/**
32 * Unit test for {@link CharStreams}.
33 *
34 * @author Chris Nokleberg
35 */
36public class CharStreamsTest extends IoTestCase {
37
38  private static final String TEXT
39      = "The quick brown fox jumped over the lazy dog.";
40
41  public void testToString() throws IOException {
42    assertEquals(TEXT, CharStreams.toString(new StringReader(TEXT)));
43  }
44
45  public void testSkipFully_blockingRead() throws IOException {
46    Reader reader = new NonSkippingReader("abcdef");
47    CharStreams.skipFully(reader, 6);
48    assertEquals(-1, reader.read());
49  }
50
51  private static class NonSkippingReader extends StringReader {
52    NonSkippingReader(String s) {
53      super(s);
54    }
55
56    @Override
57    public long skip(long n) {
58      return 0;
59    }
60  }
61
62  public void testReadLines() throws IOException {
63    List<String> lines = CharStreams.readLines(
64        new StringReader("a\nb\nc"));
65    assertEquals(ImmutableList.of("a", "b", "c"), lines);
66  }
67
68  public void testReadLines_withLineProcessor() throws IOException {
69    String text = "a\nb\nc";
70
71    // Test a LineProcessor that always returns false.
72    Reader r = new StringReader(text);
73    LineProcessor<Integer> alwaysFalse = new LineProcessor<Integer>() {
74      int seen;
75      @Override
76      public boolean processLine(String line) {
77        seen++;
78        return false;
79      }
80      @Override
81      public Integer getResult() {
82        return seen;
83      }
84    };
85    assertEquals("processLine was called more than once", 1,
86        CharStreams.readLines(r, alwaysFalse).intValue());
87
88    // Test a LineProcessor that always returns true.
89    r = new StringReader(text);
90    LineProcessor<Integer> alwaysTrue = new LineProcessor<Integer>() {
91      int seen;
92      @Override
93      public boolean processLine(String line) {
94        seen++;
95        return true;
96      }
97      @Override
98      public Integer getResult() {
99        return seen;
100      }
101    };
102    assertEquals("processLine was not called for all the lines", 3,
103        CharStreams.readLines(r, alwaysTrue).intValue());
104
105    // Test a LineProcessor that is conditional.
106    r = new StringReader(text);
107    final StringBuilder sb = new StringBuilder();
108    LineProcessor<Integer> conditional = new LineProcessor<Integer>() {
109      int seen;
110      @Override
111      public boolean processLine(String line) {
112        seen++;
113        sb.append(line);
114        return seen < 2;
115      }
116      @Override
117      public Integer getResult() {
118        return seen;
119      }
120    };
121    assertEquals(2, CharStreams.readLines(r, conditional).intValue());
122    assertEquals("ab", sb.toString());
123  }
124
125  public void testSkipFully_EOF() throws IOException {
126    Reader reader = new StringReader("abcde");
127    try {
128      CharStreams.skipFully(reader, 6);
129      fail("expected EOFException");
130    } catch (EOFException e) {
131      // expected
132    }
133  }
134
135  public void testSkipFully() throws IOException {
136    String testString = "abcdef";
137    Reader reader = new StringReader(testString);
138
139    assertEquals(testString.charAt(0), reader.read());
140    CharStreams.skipFully(reader, 1);
141    assertEquals(testString.charAt(2), reader.read());
142    CharStreams.skipFully(reader, 2);
143    assertEquals(testString.charAt(5), reader.read());
144
145    assertEquals(-1, reader.read());
146  }
147
148  public void testAsWriter() {
149    // Should wrap Appendable in a new object
150    Appendable plainAppendable = new StringBuilder();
151    Writer result = CharStreams.asWriter(plainAppendable);
152    assertNotSame(plainAppendable, result);
153    assertNotNull(result);
154
155    // A Writer should not be wrapped
156    Appendable secretlyAWriter = new StringWriter();
157    result = CharStreams.asWriter(secretlyAWriter);
158    assertSame(secretlyAWriter, result);
159  }
160
161  public void testCopy() throws IOException {
162    StringBuilder builder = new StringBuilder();
163    long copied = CharStreams.copy(new StringReader(ASCII), builder);
164    assertEquals(ASCII, builder.toString());
165    assertEquals(ASCII.length(), copied);
166
167    StringBuilder builder2 = new StringBuilder();
168    copied = CharStreams.copy(new StringReader(I18N), builder2);
169    assertEquals(I18N, builder2.toString());
170    assertEquals(I18N.length(), copied);
171  }
172
173  /**
174   * Test for Guava issue 1061: http://code.google.com/p/guava-libraries/issues/detail?id=1061
175   *
176   * <p>CharStreams.copy was failing to clear its CharBuffer after each read call, which effectively
177   * reduced the available size of the buffer each time a call to read didn't fill up the available
178   * space in the buffer completely. In general this is a performance problem since the buffer size
179   * is permanently reduced, but with certain Reader implementations it could also cause the buffer
180   * size to reach 0, causing an infinite loop.
181   */
182  public void testCopyWithReaderThatDoesNotFillBuffer() throws IOException {
183    // need a long enough string for the buffer to hit 0 remaining before the copy completes
184    String string = Strings.repeat("0123456789", 100);
185    StringBuilder b = new StringBuilder();
186    // the main assertion of this test is here... the copy will fail if the buffer size goes down
187    // each time it is not filled completely
188    long copied = CharStreams.copy(newNonBufferFillingReader(new StringReader(string)), b);
189    assertEquals(string, b.toString());
190    assertEquals(string.length(), copied);
191  }
192
193  public void testNullWriter() throws Exception {
194    // create a null writer
195    Writer nullWriter = CharStreams.nullWriter();
196    // write to the writer
197    nullWriter.write('n');
198    String test = "Test string for NullWriter";
199    nullWriter.write(test);
200    nullWriter.write(test, 2, 10);
201    // nothing really to assert?
202    assertSame(CharStreams.nullWriter(), CharStreams.nullWriter());
203  }
204
205  /**
206   * Returns a reader wrapping the given reader that only reads half of the maximum number of
207   * characters that it could read in read(char[], int, int).
208   */
209  private static Reader newNonBufferFillingReader(Reader reader) {
210    return new FilterReader(reader) {
211      @Override
212      public int read(char[] cbuf, int off, int len) throws IOException {
213        // if a buffer isn't being cleared correctly, this method will eventually start being called
214        // with a len of 0 forever
215        if (len <= 0) {
216          fail("read called with a len of " + len);
217        }
218        // read fewer than the max number of chars to read
219        // shouldn't be a problem unless the buffer is shrinking each call
220        return in.read(cbuf, off, Math.max(len - 1024, 0));
221      }
222    };
223  }
224}
225