• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 static com.google.common.io.TestOption.CLOSE_THROWS;
20 import static com.google.common.io.TestOption.OPEN_THROWS;
21 import static com.google.common.io.TestOption.READ_THROWS;
22 import static com.google.common.io.TestOption.WRITE_THROWS;
23 
24 import com.google.common.collect.ImmutableList;
25 import com.google.common.collect.ImmutableSet;
26 import com.google.common.collect.Iterables;
27 import com.google.common.collect.Lists;
28 import com.google.common.testing.TestLogHandler;
29 import java.io.BufferedReader;
30 import java.io.IOException;
31 import java.io.Reader;
32 import java.io.StringWriter;
33 import java.io.Writer;
34 import java.util.EnumSet;
35 import java.util.List;
36 import junit.framework.TestSuite;
37 
38 /**
39  * Tests for the default implementations of {@code CharSource} methods.
40  *
41  * @author Colin Decker
42  */
43 public class CharSourceTest extends IoTestCase {
44 
45   @AndroidIncompatible // Android doesn't understand suites whose tests lack default constructors.
suite()46   public static TestSuite suite() {
47     TestSuite suite = new TestSuite();
48     for (boolean asByteSource : new boolean[] {false, true}) {
49       suite.addTest(
50           CharSourceTester.tests(
51               "CharSource.wrap[CharSequence]",
52               SourceSinkFactories.stringCharSourceFactory(),
53               asByteSource));
54       suite.addTest(
55           CharSourceTester.tests(
56               "CharSource.empty[]", SourceSinkFactories.emptyCharSourceFactory(), asByteSource));
57     }
58     suite.addTestSuite(CharSourceTest.class);
59     return suite;
60   }
61 
62   private static final String STRING = ASCII + I18N;
63   private static final String LINES = "foo\nbar\r\nbaz\rsomething";
64 
65   private TestCharSource source;
66 
67   @Override
setUp()68   public void setUp() {
69     source = new TestCharSource(STRING);
70   }
71 
testOpenBufferedStream()72   public void testOpenBufferedStream() throws IOException {
73     BufferedReader reader = source.openBufferedStream();
74     assertTrue(source.wasStreamOpened());
75     assertFalse(source.wasStreamClosed());
76 
77     StringWriter writer = new StringWriter();
78     char[] buf = new char[64];
79     int read;
80     while ((read = reader.read(buf)) != -1) {
81       writer.write(buf, 0, read);
82     }
83     reader.close();
84     writer.close();
85 
86     assertTrue(source.wasStreamClosed());
87     assertEquals(STRING, writer.toString());
88   }
89 
testCopyTo_appendable()90   public void testCopyTo_appendable() throws IOException {
91     StringBuilder builder = new StringBuilder();
92 
93     assertEquals(STRING.length(), source.copyTo(builder));
94     assertTrue(source.wasStreamOpened() && source.wasStreamClosed());
95 
96     assertEquals(STRING, builder.toString());
97   }
98 
testCopyTo_charSink()99   public void testCopyTo_charSink() throws IOException {
100     TestCharSink sink = new TestCharSink();
101 
102     assertFalse(sink.wasStreamOpened() || sink.wasStreamClosed());
103 
104     assertEquals(STRING.length(), source.copyTo(sink));
105     assertTrue(source.wasStreamOpened() && source.wasStreamClosed());
106     assertTrue(sink.wasStreamOpened() && sink.wasStreamClosed());
107 
108     assertEquals(STRING, sink.getString());
109   }
110 
testRead_toString()111   public void testRead_toString() throws IOException {
112     assertEquals(STRING, source.read());
113     assertTrue(source.wasStreamOpened() && source.wasStreamClosed());
114   }
115 
testReadFirstLine()116   public void testReadFirstLine() throws IOException {
117     TestCharSource lines = new TestCharSource(LINES);
118     assertEquals("foo", lines.readFirstLine());
119     assertTrue(lines.wasStreamOpened() && lines.wasStreamClosed());
120   }
121 
testReadLines_toList()122   public void testReadLines_toList() throws IOException {
123     TestCharSource lines = new TestCharSource(LINES);
124     assertEquals(ImmutableList.of("foo", "bar", "baz", "something"), lines.readLines());
125     assertTrue(lines.wasStreamOpened() && lines.wasStreamClosed());
126   }
127 
testReadLines_withProcessor()128   public void testReadLines_withProcessor() throws IOException {
129     TestCharSource lines = new TestCharSource(LINES);
130     List<String> list =
131         lines.readLines(
132             new LineProcessor<List<String>>() {
133               List<String> list = Lists.newArrayList();
134 
135               @Override
136               public boolean processLine(String line) throws IOException {
137                 list.add(line);
138                 return true;
139               }
140 
141               @Override
142               public List<String> getResult() {
143                 return list;
144               }
145             });
146     assertEquals(ImmutableList.of("foo", "bar", "baz", "something"), list);
147     assertTrue(lines.wasStreamOpened() && lines.wasStreamClosed());
148   }
149 
testReadLines_withProcessor_stopsOnFalse()150   public void testReadLines_withProcessor_stopsOnFalse() throws IOException {
151     TestCharSource lines = new TestCharSource(LINES);
152     List<String> list =
153         lines.readLines(
154             new LineProcessor<List<String>>() {
155               List<String> list = Lists.newArrayList();
156 
157               @Override
158               public boolean processLine(String line) throws IOException {
159                 list.add(line);
160                 return false;
161               }
162 
163               @Override
164               public List<String> getResult() {
165                 return list;
166               }
167             });
168     assertEquals(ImmutableList.of("foo"), list);
169     assertTrue(lines.wasStreamOpened() && lines.wasStreamClosed());
170   }
171 
testCopyToAppendable_doesNotCloseIfWriter()172   public void testCopyToAppendable_doesNotCloseIfWriter() throws IOException {
173     TestWriter writer = new TestWriter();
174     assertFalse(writer.closed());
175     source.copyTo(writer);
176     assertFalse(writer.closed());
177   }
178 
testClosesOnErrors_copyingToCharSinkThatThrows()179   public void testClosesOnErrors_copyingToCharSinkThatThrows() {
180     for (TestOption option : EnumSet.of(OPEN_THROWS, WRITE_THROWS, CLOSE_THROWS)) {
181       TestCharSource okSource = new TestCharSource(STRING);
182       try {
183         okSource.copyTo(new TestCharSink(option));
184         fail();
185       } catch (IOException expected) {
186       }
187       // ensure reader was closed IF it was opened (depends on implementation whether or not it's
188       // opened at all if sink.newWriter() throws).
189       assertTrue(
190           "stream not closed when copying to sink with option: " + option,
191           !okSource.wasStreamOpened() || okSource.wasStreamClosed());
192     }
193   }
194 
testClosesOnErrors_whenReadThrows()195   public void testClosesOnErrors_whenReadThrows() {
196     TestCharSource failSource = new TestCharSource(STRING, READ_THROWS);
197     try {
198       failSource.copyTo(new TestCharSink());
199       fail();
200     } catch (IOException expected) {
201     }
202     assertTrue(failSource.wasStreamClosed());
203   }
204 
testClosesOnErrors_copyingToWriterThatThrows()205   public void testClosesOnErrors_copyingToWriterThatThrows() {
206     TestCharSource okSource = new TestCharSource(STRING);
207     try {
208       okSource.copyTo(new TestWriter(WRITE_THROWS));
209       fail();
210     } catch (IOException expected) {
211     }
212     assertTrue(okSource.wasStreamClosed());
213   }
214 
testConcat()215   public void testConcat() throws IOException {
216     CharSource c1 = CharSource.wrap("abc");
217     CharSource c2 = CharSource.wrap("");
218     CharSource c3 = CharSource.wrap("de");
219 
220     String expected = "abcde";
221 
222     assertEquals(expected, CharSource.concat(ImmutableList.of(c1, c2, c3)).read());
223     assertEquals(expected, CharSource.concat(c1, c2, c3).read());
224     assertEquals(expected, CharSource.concat(ImmutableList.of(c1, c2, c3).iterator()).read());
225     assertFalse(CharSource.concat(c1, c2, c3).isEmpty());
226 
227     CharSource emptyConcat = CharSource.concat(CharSource.empty(), CharSource.empty());
228     assertTrue(emptyConcat.isEmpty());
229   }
230 
testConcat_infiniteIterable()231   public void testConcat_infiniteIterable() throws IOException {
232     CharSource source = CharSource.wrap("abcd");
233     Iterable<CharSource> cycle = Iterables.cycle(ImmutableList.of(source));
234     CharSource concatenated = CharSource.concat(cycle);
235 
236     String expected = "abcdabcd";
237 
238     // read the first 8 chars manually, since there's no equivalent to ByteSource.slice
239     // TODO(cgdecker): Add CharSource.slice?
240     StringBuilder builder = new StringBuilder();
241     Reader reader = concatenated.openStream(); // no need to worry about closing
242     for (int i = 0; i < 8; i++) {
243       builder.append((char) reader.read());
244     }
245     assertEquals(expected, builder.toString());
246   }
247 
248   static final CharSource BROKEN_READ_SOURCE = new TestCharSource("ABC", READ_THROWS);
249   static final CharSource BROKEN_CLOSE_SOURCE = new TestCharSource("ABC", CLOSE_THROWS);
250   static final CharSource BROKEN_OPEN_SOURCE = new TestCharSource("ABC", OPEN_THROWS);
251   static final CharSink BROKEN_WRITE_SINK = new TestCharSink(WRITE_THROWS);
252   static final CharSink BROKEN_CLOSE_SINK = new TestCharSink(CLOSE_THROWS);
253   static final CharSink BROKEN_OPEN_SINK = new TestCharSink(OPEN_THROWS);
254 
255   private static final ImmutableSet<CharSource> BROKEN_SOURCES =
256       ImmutableSet.of(BROKEN_CLOSE_SOURCE, BROKEN_OPEN_SOURCE, BROKEN_READ_SOURCE);
257   private static final ImmutableSet<CharSink> BROKEN_SINKS =
258       ImmutableSet.of(BROKEN_CLOSE_SINK, BROKEN_OPEN_SINK, BROKEN_WRITE_SINK);
259 
testCopyExceptions()260   public void testCopyExceptions() {
261     if (!Closer.SuppressingSuppressor.isAvailable()) {
262       // test that exceptions are logged
263 
264       TestLogHandler logHandler = new TestLogHandler();
265       Closeables.logger.addHandler(logHandler);
266       try {
267         for (CharSource in : BROKEN_SOURCES) {
268           runFailureTest(in, newNormalCharSink());
269           assertTrue(logHandler.getStoredLogRecords().isEmpty());
270 
271           runFailureTest(in, BROKEN_CLOSE_SINK);
272           assertEquals((in == BROKEN_OPEN_SOURCE) ? 0 : 1, getAndResetRecords(logHandler));
273         }
274 
275         for (CharSink out : BROKEN_SINKS) {
276           runFailureTest(newNormalCharSource(), out);
277           assertTrue(logHandler.getStoredLogRecords().isEmpty());
278 
279           runFailureTest(BROKEN_CLOSE_SOURCE, out);
280           assertEquals(1, getAndResetRecords(logHandler));
281         }
282 
283         for (CharSource in : BROKEN_SOURCES) {
284           for (CharSink out : BROKEN_SINKS) {
285             runFailureTest(in, out);
286             assertTrue(getAndResetRecords(logHandler) <= 1);
287           }
288         }
289       } finally {
290         Closeables.logger.removeHandler(logHandler);
291       }
292     } else {
293       // test that exceptions are suppressed
294 
295       for (CharSource in : BROKEN_SOURCES) {
296         int suppressed = runSuppressionFailureTest(in, newNormalCharSink());
297         assertEquals(0, suppressed);
298 
299         suppressed = runSuppressionFailureTest(in, BROKEN_CLOSE_SINK);
300         assertEquals((in == BROKEN_OPEN_SOURCE) ? 0 : 1, suppressed);
301       }
302 
303       for (CharSink out : BROKEN_SINKS) {
304         int suppressed = runSuppressionFailureTest(newNormalCharSource(), out);
305         assertEquals(0, suppressed);
306 
307         suppressed = runSuppressionFailureTest(BROKEN_CLOSE_SOURCE, out);
308         assertEquals(1, suppressed);
309       }
310 
311       for (CharSource in : BROKEN_SOURCES) {
312         for (CharSink out : BROKEN_SINKS) {
313           int suppressed = runSuppressionFailureTest(in, out);
314           assertTrue(suppressed <= 1);
315         }
316       }
317     }
318   }
319 
getAndResetRecords(TestLogHandler logHandler)320   private static int getAndResetRecords(TestLogHandler logHandler) {
321     int records = logHandler.getStoredLogRecords().size();
322     logHandler.clear();
323     return records;
324   }
325 
runFailureTest(CharSource in, CharSink out)326   private static void runFailureTest(CharSource in, CharSink out) {
327     try {
328       in.copyTo(out);
329       fail();
330     } catch (IOException expected) {
331     }
332   }
333 
334   /** @return the number of exceptions that were suppressed on the expected thrown exception */
runSuppressionFailureTest(CharSource in, CharSink out)335   private static int runSuppressionFailureTest(CharSource in, CharSink out) {
336     try {
337       in.copyTo(out);
338       fail();
339     } catch (IOException expected) {
340       return CloserTest.getSuppressed(expected).length;
341     }
342     throw new AssertionError(); // can't happen
343   }
344 
newNormalCharSource()345   private static CharSource newNormalCharSource() {
346     return CharSource.wrap("ABC");
347   }
348 
newNormalCharSink()349   private static CharSink newNormalCharSink() {
350     return new CharSink() {
351       @Override
352       public Writer openStream() {
353         return new StringWriter();
354       }
355     };
356   }
357 }
358