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