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 import java.io.EOFException; 22 import java.io.FilterReader; 23 import java.io.IOException; 24 import java.io.Reader; 25 import java.io.StringReader; 26 import java.io.StringWriter; 27 import java.io.Writer; 28 import java.nio.CharBuffer; 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 = "The quick brown fox jumped over the lazy dog."; 39 testToString()40 public void testToString() throws IOException { 41 assertEquals(TEXT, CharStreams.toString(new StringReader(TEXT))); 42 } 43 testReadLines()44 public void testReadLines() throws IOException { 45 List<String> lines = CharStreams.readLines(new StringReader("a\nb\nc")); 46 assertEquals(ImmutableList.of("a", "b", "c"), lines); 47 } 48 testReadLines_withLineProcessor()49 public void testReadLines_withLineProcessor() throws IOException { 50 String text = "a\nb\nc"; 51 52 // Test a LineProcessor that always returns false. 53 Reader r = new StringReader(text); 54 LineProcessor<Integer> alwaysFalse = 55 new LineProcessor<Integer>() { 56 int seen; 57 58 @Override 59 public boolean processLine(String line) { 60 seen++; 61 return false; 62 } 63 64 @Override 65 public Integer getResult() { 66 return seen; 67 } 68 }; 69 assertEquals( 70 "processLine was called more than once", 71 1, 72 CharStreams.readLines(r, alwaysFalse).intValue()); 73 74 // Test a LineProcessor that always returns true. 75 r = new StringReader(text); 76 LineProcessor<Integer> alwaysTrue = 77 new LineProcessor<Integer>() { 78 int seen; 79 80 @Override 81 public boolean processLine(String line) { 82 seen++; 83 return true; 84 } 85 86 @Override 87 public Integer getResult() { 88 return seen; 89 } 90 }; 91 assertEquals( 92 "processLine was not called for all the lines", 93 3, 94 CharStreams.readLines(r, alwaysTrue).intValue()); 95 96 // Test a LineProcessor that is conditional. 97 r = new StringReader(text); 98 final StringBuilder sb = new StringBuilder(); 99 LineProcessor<Integer> conditional = 100 new LineProcessor<Integer>() { 101 int seen; 102 103 @Override 104 public boolean processLine(String line) { 105 seen++; 106 sb.append(line); 107 return seen < 2; 108 } 109 110 @Override 111 public Integer getResult() { 112 return seen; 113 } 114 }; 115 assertEquals(2, CharStreams.readLines(r, conditional).intValue()); 116 assertEquals("ab", sb.toString()); 117 } 118 testSkipFully_EOF()119 public void testSkipFully_EOF() throws IOException { 120 Reader reader = new StringReader("abcde"); 121 try { 122 CharStreams.skipFully(reader, 6); 123 fail("expected EOFException"); 124 } catch (EOFException expected) { 125 } 126 } 127 testSkipFully()128 public void testSkipFully() throws IOException { 129 String testString = "abcdef"; 130 Reader reader = new StringReader(testString); 131 132 assertEquals(testString.charAt(0), reader.read()); 133 CharStreams.skipFully(reader, 1); 134 assertEquals(testString.charAt(2), reader.read()); 135 CharStreams.skipFully(reader, 2); 136 assertEquals(testString.charAt(5), reader.read()); 137 138 assertEquals(-1, reader.read()); 139 } 140 testAsWriter()141 public void testAsWriter() { 142 // Should wrap Appendable in a new object 143 Appendable plainAppendable = new StringBuilder(); 144 Writer result = CharStreams.asWriter(plainAppendable); 145 assertNotSame(plainAppendable, result); 146 assertNotNull(result); 147 148 // A Writer should not be wrapped 149 Appendable secretlyAWriter = new StringWriter(); 150 result = CharStreams.asWriter(secretlyAWriter); 151 assertSame(secretlyAWriter, result); 152 } 153 154 // CharStreams.copy has type specific optimizations for Readers,StringBuilders and Writers 155 testCopy()156 public void testCopy() throws IOException { 157 StringBuilder builder = new StringBuilder(); 158 long copied = 159 CharStreams.copy( 160 wrapAsGenericReadable(new StringReader(ASCII)), wrapAsGenericAppendable(builder)); 161 assertEquals(ASCII, builder.toString()); 162 assertEquals(ASCII.length(), copied); 163 164 StringBuilder builder2 = new StringBuilder(); 165 copied = 166 CharStreams.copy( 167 wrapAsGenericReadable(new StringReader(I18N)), wrapAsGenericAppendable(builder2)); 168 assertEquals(I18N, builder2.toString()); 169 assertEquals(I18N.length(), copied); 170 } 171 testCopy_toStringBuilder_fromReader()172 public void testCopy_toStringBuilder_fromReader() throws IOException { 173 StringBuilder builder = new StringBuilder(); 174 long copied = CharStreams.copy(new StringReader(ASCII), builder); 175 assertEquals(ASCII, builder.toString()); 176 assertEquals(ASCII.length(), copied); 177 178 StringBuilder builder2 = new StringBuilder(); 179 copied = CharStreams.copy(new StringReader(I18N), builder2); 180 assertEquals(I18N, builder2.toString()); 181 assertEquals(I18N.length(), copied); 182 } 183 testCopy_toStringBuilder_fromReadable()184 public void testCopy_toStringBuilder_fromReadable() throws IOException { 185 StringBuilder builder = new StringBuilder(); 186 long copied = CharStreams.copy(wrapAsGenericReadable(new StringReader(ASCII)), builder); 187 assertEquals(ASCII, builder.toString()); 188 assertEquals(ASCII.length(), copied); 189 190 StringBuilder builder2 = new StringBuilder(); 191 copied = CharStreams.copy(wrapAsGenericReadable(new StringReader(I18N)), builder2); 192 assertEquals(I18N, builder2.toString()); 193 assertEquals(I18N.length(), copied); 194 } 195 testCopy_toWriter_fromReader()196 public void testCopy_toWriter_fromReader() throws IOException { 197 StringWriter writer = new StringWriter(); 198 long copied = CharStreams.copy(new StringReader(ASCII), writer); 199 assertEquals(ASCII, writer.toString()); 200 assertEquals(ASCII.length(), copied); 201 202 StringWriter writer2 = new StringWriter(); 203 copied = CharStreams.copy(new StringReader(I18N), writer2); 204 assertEquals(I18N, writer2.toString()); 205 assertEquals(I18N.length(), copied); 206 } 207 testCopy_toWriter_fromReadable()208 public void testCopy_toWriter_fromReadable() throws IOException { 209 StringWriter writer = new StringWriter(); 210 long copied = CharStreams.copy(wrapAsGenericReadable(new StringReader(ASCII)), writer); 211 assertEquals(ASCII, writer.toString()); 212 assertEquals(ASCII.length(), copied); 213 214 StringWriter writer2 = new StringWriter(); 215 copied = CharStreams.copy(wrapAsGenericReadable(new StringReader(I18N)), writer2); 216 assertEquals(I18N, writer2.toString()); 217 assertEquals(I18N.length(), copied); 218 } 219 220 /** 221 * Test for Guava issue 1061: http://code.google.com/p/guava-libraries/issues/detail?id=1061 222 * 223 * <p>CharStreams.copy was failing to clear its CharBuffer after each read call, which effectively 224 * reduced the available size of the buffer each time a call to read didn't fill up the available 225 * space in the buffer completely. In general this is a performance problem since the buffer size 226 * is permanently reduced, but with certain Reader implementations it could also cause the buffer 227 * size to reach 0, causing an infinite loop. 228 */ testCopyWithReaderThatDoesNotFillBuffer()229 public void testCopyWithReaderThatDoesNotFillBuffer() throws IOException { 230 // need a long enough string for the buffer to hit 0 remaining before the copy completes 231 String string = Strings.repeat("0123456789", 100); 232 StringBuilder b = new StringBuilder(); 233 // the main assertion of this test is here... the copy will fail if the buffer size goes down 234 // each time it is not filled completely 235 long copied = CharStreams.copy(newNonBufferFillingReader(new StringReader(string)), b); 236 assertEquals(string, b.toString()); 237 assertEquals(string.length(), copied); 238 } 239 testExhaust_reader()240 public void testExhaust_reader() throws IOException { 241 Reader reader = new StringReader(ASCII); 242 assertEquals(ASCII.length(), CharStreams.exhaust(reader)); 243 assertEquals(-1, reader.read()); 244 assertEquals(0, CharStreams.exhaust(reader)); 245 246 Reader empty = new StringReader(""); 247 assertEquals(0, CharStreams.exhaust(empty)); 248 assertEquals(-1, empty.read()); 249 } 250 testExhaust_readable()251 public void testExhaust_readable() throws IOException { 252 CharBuffer buf = CharBuffer.wrap(ASCII); 253 assertEquals(ASCII.length(), CharStreams.exhaust(buf)); 254 assertEquals(0, buf.remaining()); 255 assertEquals(0, CharStreams.exhaust(buf)); 256 257 CharBuffer empty = CharBuffer.wrap(""); 258 assertEquals(0, CharStreams.exhaust(empty)); 259 assertEquals(0, empty.remaining()); 260 } 261 testNullWriter()262 public void testNullWriter() throws Exception { 263 // create a null writer 264 Writer nullWriter = CharStreams.nullWriter(); 265 // write to the writer 266 nullWriter.write('n'); 267 String test = "Test string for NullWriter"; 268 nullWriter.write(test); 269 nullWriter.write(test, 2, 10); 270 nullWriter.append(null); 271 nullWriter.append(null, 0, 4); 272 273 try { 274 nullWriter.append(null, -1, 4); 275 fail(); 276 } catch (IndexOutOfBoundsException expected) { 277 } 278 279 try { 280 nullWriter.append(null, 0, 5); 281 fail(); 282 } catch (IndexOutOfBoundsException expected) { 283 } 284 285 // nothing really to assert? 286 assertSame(CharStreams.nullWriter(), CharStreams.nullWriter()); 287 } 288 289 /** 290 * Returns a reader wrapping the given reader that only reads half of the maximum number of 291 * characters that it could read in read(char[], int, int). 292 */ newNonBufferFillingReader(Reader reader)293 private static Reader newNonBufferFillingReader(Reader reader) { 294 return new FilterReader(reader) { 295 @Override 296 public int read(char[] cbuf, int off, int len) throws IOException { 297 // if a buffer isn't being cleared correctly, this method will eventually start being called 298 // with a len of 0 forever 299 if (len <= 0) { 300 fail("read called with a len of " + len); 301 } 302 // read fewer than the max number of chars to read 303 // shouldn't be a problem unless the buffer is shrinking each call 304 return in.read(cbuf, off, Math.max(len - 1024, 0)); 305 } 306 }; 307 } 308 309 /** Wrap an appendable in an appendable to defeat any type specific optimizations. */ 310 private static Appendable wrapAsGenericAppendable(final Appendable a) { 311 return new Appendable() { 312 313 @Override 314 public Appendable append(CharSequence csq) throws IOException { 315 a.append(csq); 316 return this; 317 } 318 319 @Override 320 public Appendable append(CharSequence csq, int start, int end) throws IOException { 321 a.append(csq, start, end); 322 return this; 323 } 324 325 @Override 326 public Appendable append(char c) throws IOException { 327 a.append(c); 328 return this; 329 } 330 }; 331 } 332 333 /** Wrap a readable in a readable to defeat any type specific optimizations. */ 334 private static Readable wrapAsGenericReadable(final Readable a) { 335 return new Readable() { 336 @Override 337 public int read(CharBuffer cb) throws IOException { 338 return a.read(cb); 339 } 340 }; 341 } 342 } 343