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