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