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 
17 package com.google.common.io;
18 
19 import com.google.common.base.Strings;
20 import com.google.common.collect.ImmutableList;
21 
22 import java.io.EOFException;
23 import java.io.FilterReader;
24 import java.io.IOException;
25 import java.io.Reader;
26 import java.io.StringReader;
27 import java.io.StringWriter;
28 import java.io.Writer;
29 import java.util.List;
30 
31 /**
32  * Unit test for {@link CharStreams}.
33  *
34  * @author Chris Nokleberg
35  */
36 public class CharStreamsTest extends IoTestCase {
37 
38   private static final String TEXT
39       = "The quick brown fox jumped over the lazy dog.";
40 
testToString()41   public void testToString() throws IOException {
42     assertEquals(TEXT, CharStreams.toString(new StringReader(TEXT)));
43   }
44 
testSkipFully_blockingRead()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 {
NonSkippingReader(String s)52     NonSkippingReader(String s) {
53       super(s);
54     }
55 
56     @Override
skip(long n)57     public long skip(long n) {
58       return 0;
59     }
60   }
61 
testReadLines()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 
testReadLines_withLineProcessor()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 
testSkipFully_EOF()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 
testSkipFully()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 
testAsWriter()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 
testCopy()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    */
testCopyWithReaderThatDoesNotFillBuffer()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 
testNullWriter()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    */
newNonBufferFillingReader(Reader reader)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