1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package org.apache.harmony.luni.tests.java.io;
19
20import java.io.ByteArrayInputStream;
21import java.io.ByteArrayOutputStream;
22import java.io.File;
23import java.io.FileInputStream;
24import java.io.FileReader;
25import java.io.FileWriter;
26import java.io.IOException;
27import java.io.InputStreamReader;
28import java.io.OutputStreamWriter;
29import java.io.UnsupportedEncodingException;
30import java.nio.charset.Charset;
31import java.nio.charset.CharsetEncoder;
32
33import junit.framework.TestCase;
34
35public class OutputStreamWriterTest extends TestCase {
36
37    private static final int UPPER = 0xd800;
38
39    private static final int BUFFER_SIZE = 10000;
40
41    private ByteArrayOutputStream out;
42
43    private OutputStreamWriter writer;
44
45    static private final String source = "This is a test message with Unicode character. \u4e2d\u56fd is China's name in Chinese";
46
47    static private final String[] MINIMAL_CHARSETS = new String[] { "US-ASCII",
48            "ISO-8859-1", "UTF-16BE", "UTF-16LE", "UTF-16", "UTF-8" };
49
50    OutputStreamWriter osw;
51
52    InputStreamReader isr;
53
54    private ByteArrayOutputStream fos;
55
56    String testString = "Test_All_Tests\nTest_java_io_BufferedInputStream\nTest_java_io_BufferedOutputStream\nTest_java_io_ByteArrayInputStream\nTest_java_io_ByteArrayOutputStream\nTest_java_io_DataInputStream\n";
57
58    /*
59     * @see TestCase#setUp()
60     */
61    @Override
62    protected void setUp() throws Exception {
63        super.setUp();
64        out = new ByteArrayOutputStream();
65        writer = new OutputStreamWriter(out, "utf-8");
66
67        fos = new ByteArrayOutputStream();
68        osw = new OutputStreamWriter(fos);
69    }
70
71    /*
72     * @see TestCase#tearDown()
73     */
74    @Override
75    protected void tearDown() throws Exception {
76        try {
77            writer.close();
78
79            if (isr != null) {
80                isr.close();
81            }
82            osw.close();
83        } catch (Exception e) {
84            // Ignored
85        }
86
87        super.tearDown();
88    }
89
90    public void testClose() throws Exception {
91        writer.flush();
92        writer.close();
93        try {
94            writer.flush();
95            fail();
96        } catch (IOException e) {
97            // Expected
98        }
99    }
100
101    public void testFlush() throws Exception {
102        writer.write(source);
103        writer.flush();
104        String result = out.toString("utf-8");
105        assertEquals(source, result);
106    }
107
108    /*
109     * Class under test for void write(char[], int, int)
110     */
111    public void testWritecharArrayintint() throws IOException {
112        char[] chars = source.toCharArray();
113
114        // Throws IndexOutOfBoundsException if offset is negative
115        try {
116            writer.write((char[]) null, -1, -1);
117            fail();
118        } catch (NullPointerException exception) {
119        } catch (IndexOutOfBoundsException exception) {
120        }
121
122        // throws NullPointerException though count is negative
123        try {
124            writer.write((char[]) null, 1, -1);
125            fail();
126        } catch (NullPointerException exception) {
127        } catch (IndexOutOfBoundsException exception) {
128        }
129
130        try {
131            writer.write((char[]) null, 1, 1);
132            fail();
133        } catch (NullPointerException e) {
134            // Expected
135        }
136        try {
137            writer.write(new char[0], 0, 1);
138            fail();
139        } catch (IndexOutOfBoundsException e) {
140            // Expected
141        }
142        try {
143            writer.write(chars, -1, 1);
144            fail();
145        } catch (IndexOutOfBoundsException e) {
146            // Expected
147        }
148        try {
149            writer.write(chars, 0, -1);
150            fail();
151        } catch (IndexOutOfBoundsException e) {
152            // Expected
153        }
154        try {
155            writer.write(chars, 1, chars.length);
156            fail();
157        } catch (IndexOutOfBoundsException e) {
158            // Expected
159        }
160        writer.write(chars, 1, 2);
161        writer.flush();
162        assertEquals("hi", out.toString("utf-8"));
163        writer.write(chars, 0, chars.length);
164        writer.flush();
165        assertEquals("hi" + source, out.toString("utf-8"));
166
167        writer.close();
168        // After the stream is closed, should throw IOException first
169        try {
170            writer.write((char[]) null, -1, -1);
171            fail("should throw IOException");
172        } catch (IOException e) {
173            // Expected
174        }
175    }
176
177    /*
178     * Class under test for void write(int)
179     */
180    public void testWriteint() throws IOException {
181        writer.write(1);
182        writer.flush();
183        String str = new String(out.toByteArray(), "utf-8");
184        assertEquals("\u0001", str);
185
186        writer.write(2);
187        writer.flush();
188        str = new String(out.toByteArray(), "utf-8");
189        assertEquals("\u0001\u0002", str);
190
191        writer.write(-1);
192        writer.flush();
193        str = new String(out.toByteArray(), "utf-8");
194        assertEquals("\u0001\u0002\uffff", str);
195
196        writer.write(0xfedcb);
197        writer.flush();
198        str = new String(out.toByteArray(), "utf-8");
199        assertEquals("\u0001\u0002\uffff\uedcb", str);
200
201        writer.close();
202        // After the stream is closed, should throw IOException
203        try {
204            writer.write(1);
205            fail("should throw IOException");
206        } catch (IOException e) {
207            // expected
208        }
209    }
210
211    /*
212     * Class under test for void write(String, int, int)
213     */
214    public void testWriteStringintint() throws IOException {
215        try {
216            writer.write((String) null, 1, 1);
217            fail();
218        } catch (NullPointerException e) {
219            // Expected
220        }
221        try {
222            writer.write("", 0, 1);
223            fail();
224        } catch (StringIndexOutOfBoundsException e) {
225            // Expected
226        }
227        try {
228            writer.write("abc", -1, 1);
229            fail();
230        } catch (StringIndexOutOfBoundsException e) {
231            // Expected
232        }
233        try {
234            writer.write("abc", 0, -1);
235            fail();
236        } catch (IndexOutOfBoundsException e) {
237            // Expected
238        }
239        try {
240            writer.write("abc", 1, 3);
241            fail();
242        } catch (StringIndexOutOfBoundsException e) {
243            // Expected
244        }
245
246        try {
247            writer.write((String) null, -1, -1);
248            fail();
249        } catch (NullPointerException expected) {
250        } catch (IndexOutOfBoundsException expected) {
251        }
252
253        // Throws NullPointerException before StringIndexOutOfBoundsException
254        try {
255            writer.write((String) null, -1, 0);
256            fail();
257        } catch (NullPointerException expected) {
258        } catch (IndexOutOfBoundsException expected) {
259        }
260
261        writer.write("abc", 1, 2);
262        writer.flush();
263        assertEquals("bc", out.toString("utf-8"));
264        writer.write(source, 0, source.length());
265        writer.flush();
266        assertEquals("bc" + source, out.toString("utf-8"));
267
268        writer.close();
269        // Throws IndexOutOfBoundsException first if count is negative
270        try {
271            writer.write((String) null, 0, -1);
272            fail();
273        } catch (NullPointerException expected) {
274        } catch (IndexOutOfBoundsException expected) {
275        }
276
277        try {
278            writer.write((String) null, -1, 0);
279            fail();
280        } catch (NullPointerException expected) {
281        } catch (IndexOutOfBoundsException expected) {
282        }
283
284        try {
285            writer.write("abc", -1, 0);
286            fail("should throw StringIndexOutOfBoundsException");
287        } catch (StringIndexOutOfBoundsException e) {
288            // Expected
289        }
290
291        // Throws IOException
292        try {
293            writer.write("abc", 0, 1);
294            fail("should throw IOException");
295        } catch (IOException e) {
296            // expected
297        }
298    }
299
300    /*
301     * Class under test for void OutputStreamWriter(OutputStream)
302     */
303    public void testOutputStreamWriterOutputStream() throws IOException {
304        try {
305            writer = new OutputStreamWriter(null);
306            fail();
307        } catch (NullPointerException e) {
308            // Expected
309        }
310        OutputStreamWriter writer2 = new OutputStreamWriter(out);
311        writer2.close();
312    }
313
314    /*
315     * Class under test for void OutputStreamWriter(OutputStream, String)
316     */
317    public void testOutputStreamWriterOutputStreamString() throws IOException {
318        try {
319            writer = new OutputStreamWriter(null, "utf-8");
320            fail();
321        } catch (NullPointerException e) {
322            // Expected
323        }
324        try {
325            writer = new OutputStreamWriter(out, "");
326            fail();
327        } catch (UnsupportedEncodingException e) {
328            // Expected
329        }
330        try {
331            writer = new OutputStreamWriter(out, "badname");
332            fail();
333        } catch (UnsupportedEncodingException e) {
334            // Expected
335        }
336        try {
337            writer = new OutputStreamWriter(out, (String) null);
338            fail();
339        } catch (NullPointerException e) {
340            // Expected
341        }
342        OutputStreamWriter writer2 = new OutputStreamWriter(out, "ascii");
343        assertEquals(Charset.forName("ascii"), Charset.forName(writer2
344                .getEncoding()));
345        writer2.close();
346    }
347
348    /*
349     * Class under test for void OutputStreamWriter(OutputStream)
350     */
351    public void testOutputStreamWriterOutputStreamCharset() throws IOException {
352        Charset cs = Charset.forName("ascii");
353        try {
354            writer = new OutputStreamWriter(null, cs);
355            fail();
356        } catch (NullPointerException e) {
357            // Expected
358        }
359        try {
360            writer = new OutputStreamWriter(out, (Charset) null);
361            fail();
362        } catch (NullPointerException e) {
363            // Expected
364        }
365        OutputStreamWriter writer2 = new OutputStreamWriter(out, cs);
366        assertEquals(cs, Charset.forName(writer2.getEncoding()));
367        writer2.close();
368    }
369
370    /*
371     * Class under test for void OutputStreamWriter(OutputStream, String)
372     */
373    public void testOutputStreamWriterOutputStreamCharsetEncoder()
374            throws IOException {
375        Charset cs = Charset.forName("ascii");
376        CharsetEncoder enc = cs.newEncoder();
377        try {
378            writer = new OutputStreamWriter(null, enc);
379            fail();
380        } catch (NullPointerException e) {
381            // Expected
382        }
383        try {
384            writer = new OutputStreamWriter(out, (CharsetEncoder) null);
385            fail();
386        } catch (NullPointerException e) {
387            // Expected
388        }
389        OutputStreamWriter writer2 = new OutputStreamWriter(out, enc);
390        assertEquals(cs, Charset.forName(writer2.getEncoding()));
391        writer2.close();
392    }
393
394    public void testGetEncoding() {
395        Charset cs = Charset.forName("utf-8");
396        assertEquals(cs, Charset.forName(writer.getEncoding()));
397    }
398
399    public void testHandleEarlyEOFChar_1() throws IOException {
400        String str = "All work and no play makes Jack a dull boy\n"; //$NON-NLS-1$
401        int NUMBER = 2048;
402        int j = 0;
403        int len = str.length() * NUMBER;
404        char[] strChars = new char[len];
405        for (int i = 0; i < NUMBER; ++i) {
406            for (int k = 0; k < str.length(); ++k) {
407                strChars[j++] = str.charAt(k);
408            }
409        }
410
411        File f = File.createTempFile("one", "by_one");
412        f.deleteOnExit();
413        FileWriter fw = new FileWriter(f);
414        fw.write(strChars);
415        fw.close();
416        FileInputStream fis = new FileInputStream(f);
417        InputStreamReader in = new InputStreamReader(fis);
418        for (int offset = 0; offset < strChars.length; ++offset) {
419            int b = in.read();
420            assertFalse("Early EOF at offset", -1 == b);
421        }
422    }
423
424    public void testHandleEarlyEOFChar_2() throws IOException {
425        int capacity = 65536;
426        byte[] bytes = new byte[capacity];
427        byte[] bs = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' };
428        for (int i = 0; i < bytes.length; i++) {
429            bytes[i] = bs[i / 8192];
430        }
431        String inputStr = new String(bytes);
432        int len = inputStr.length();
433        File f = File.createTempFile("FileWriterBugTest ", null); //$NON-NLS-1$
434        f.deleteOnExit();
435        FileWriter writer = new FileWriter(f);
436        writer.write(inputStr);
437        writer.close();
438        long flen = f.length();
439
440        FileReader reader = new FileReader(f);
441        char[] outChars = new char[capacity];
442        int outCount = reader.read(outChars);
443        String outStr = new String(outChars, 0, outCount);
444
445        assertEquals(len, flen);
446        assertEquals(inputStr, outStr);
447    }
448
449    public void testSingleCharIO() throws Exception {
450        InputStreamReader isr = null;
451        for (int i = 0; i < MINIMAL_CHARSETS.length; ++i) {
452            try {
453                out = new ByteArrayOutputStream();
454                writer = new OutputStreamWriter(out, MINIMAL_CHARSETS[i]);
455
456                int upper = UPPER;
457                switch (i) {
458                case 0:
459                    upper = 128;
460                    break;
461                case 1:
462                    upper = 256;
463                    break;
464                }
465
466                for (int c = 0; c < upper; ++c) {
467                    writer.write(c);
468                }
469                writer.flush();
470                byte[] result = out.toByteArray();
471
472                isr = new InputStreamReader(new ByteArrayInputStream(result),
473                        MINIMAL_CHARSETS[i]);
474                for (int expected = 0; expected < upper; ++expected) {
475                    assertEquals("Error when reading bytes in "
476                            + MINIMAL_CHARSETS[i], expected, isr.read());
477                }
478            } finally {
479                try {
480                    isr.close();
481                } catch (Exception e) {
482                }
483                try {
484                    writer.close();
485                } catch (Exception e) {
486                }
487            }
488        }
489    }
490
491    public void testBlockIO() throws Exception {
492        InputStreamReader isr = null;
493        char[] largeBuffer = new char[BUFFER_SIZE];
494        for (int i = 0; i < MINIMAL_CHARSETS.length; ++i) {
495            try {
496                out = new ByteArrayOutputStream();
497                writer = new OutputStreamWriter(out, MINIMAL_CHARSETS[i]);
498
499                int upper = UPPER;
500                switch (i) {
501                case 0:
502                    upper = 128;
503                    break;
504                case 1:
505                    upper = 256;
506                    break;
507                }
508
509                int m = 0;
510                for (int c = 0; c < upper; ++c) {
511                    largeBuffer[m++] = (char) c;
512                    if (m == BUFFER_SIZE) {
513                        writer.write(largeBuffer);
514                        m = 0;
515                    }
516                }
517                writer.write(largeBuffer, 0, m);
518                writer.flush();
519                byte[] result = out.toByteArray();
520
521                isr = new InputStreamReader(new ByteArrayInputStream(result),
522                        MINIMAL_CHARSETS[i]);
523                int expected = 0, read = 0, j = 0;
524                while (expected < upper) {
525                    if (j == read) {
526                        read = isr.read(largeBuffer);
527                        j = 0;
528                    }
529                    assertEquals("Error when reading bytes in "
530                            + MINIMAL_CHARSETS[i], expected++, largeBuffer[j++]);
531                }
532            } finally {
533                try {
534                    isr.close();
535                } catch (Exception e) {
536                }
537                try {
538                    writer.close();
539                } catch (Exception e) {
540                }
541            }
542        }
543    }
544
545    /**
546     * @tests java.io.OutputStreamWriter#OutputStreamWriter(java.io.OutputStream)
547     */
548    public void test_ConstructorLjava_io_OutputStream() {
549        assertTrue("Used in tests", true);
550    }
551
552    /**
553     * @tests java.io.OutputStreamWriter#OutputStreamWriter(java.io.OutputStream,
554     *        java.lang.String)
555     */
556    public void test_ConstructorLjava_io_OutputStreamLjava_lang_String()
557            throws UnsupportedEncodingException {
558        osw = new OutputStreamWriter(fos, "8859_1");
559        try {
560            osw = new OutputStreamWriter(fos, "Bogus");
561            fail("Failed to throw Unsupported Encoding exception");
562        } catch (UnsupportedEncodingException e) {
563            // Expected
564        }
565    }
566
567    /**
568     * @tests java.io.OutputStreamWriter#close()
569     */
570    public void test_close() throws IOException {
571        osw.close();
572
573        try {
574            osw.write(testString, 0, testString.length());
575            fail("Chars written after close");
576        } catch (IOException e) {
577            // Expected
578        }
579
580        ByteArrayOutputStream bout = new ByteArrayOutputStream();
581        try {
582            OutputStreamWriter writer = new OutputStreamWriter(bout,
583                    "ISO2022JP");
584            writer.write(new char[] { 'a' });
585            writer.close();
586            // the default is ASCII, there should not be any mode changes
587            String converted = new String(bout.toByteArray(), "ISO8859_1");
588            assertTrue("invalid conversion 1: " + converted, converted
589                    .equals("a"));
590
591            bout.reset();
592            writer = new OutputStreamWriter(bout, "ISO2022JP");
593            writer.write(new char[] { '\u3048' });
594            writer.flush();
595            // the byte sequence should not switch to ASCII mode until the
596            // stream is closed
597            converted = new String(bout.toByteArray(), "ISO8859_1");
598            assertTrue("invalid conversion 2: " + converted, converted
599                    .equals("\u001b$B$("));
600            writer.close();
601            converted = new String(bout.toByteArray(), "ISO8859_1");
602            assertTrue("invalid conversion 3: " + converted, converted
603                    .equals("\u001b$B$(\u001b(B"));
604
605            bout.reset();
606            writer = new OutputStreamWriter(bout, "ISO2022JP");
607            writer.write(new char[] { '\u3048' });
608            writer.write(new char[] { '\u3048' });
609            writer.close();
610            // there should not be a mode switch between writes
611            assertEquals("invalid conversion 4", "\u001b$B$($(\u001b(B",
612                    new String(bout.toByteArray(), "ISO8859_1"));
613        } catch (UnsupportedEncodingException e) {
614            // Can't test missing converter
615            System.out.println(e);
616        }
617    }
618
619    /**
620     * @tests java.io.OutputStreamWriter#flush()
621     */
622    public void test_flush() throws IOException {
623        char[] buf = new char[testString.length()];
624        osw.write(testString, 0, testString.length());
625        osw.flush();
626        openInputStream();
627        isr.read(buf, 0, buf.length);
628        assertTrue("Chars not flushed", new String(buf, 0, buf.length)
629                .equals(testString));
630    }
631
632    /**
633     * @tests java.io.OutputStreamWriter#getEncoding()
634     */
635    public void test_getEncoding() throws IOException {
636        try {
637            osw = new OutputStreamWriter(fos, "8859_1");
638        } catch (UnsupportedEncodingException e) {
639            assertEquals("Returned incorrect encoding", "8859_1", osw
640                    .getEncoding());
641        }
642
643        OutputStreamWriter out = new OutputStreamWriter(
644                new ByteArrayOutputStream(), "UTF-16BE");
645        out.close();
646
647        String result = out.getEncoding();
648        assertNull(result);
649
650        out = null;
651        try {
652            out = new OutputStreamWriter(new ByteArrayOutputStream(),
653                    "UTF-16BE");
654        } catch (UnsupportedEncodingException e) {
655            // ok
656        }
657        result = out.getEncoding();
658        assertEquals("UnicodeBigUnmarked", result);
659    }
660
661    /**
662     * @tests java.io.OutputStreamWriter#write(char[], int, int)
663     */
664    public void test_write$CII() throws IOException {
665        char[] buf = new char[testString.length()];
666        osw.write(testString, 0, testString.length());
667        osw.close();
668        openInputStream();
669        isr.read(buf, 0, buf.length);
670        assertTrue("Incorrect chars returned", new String(buf, 0, buf.length)
671                .equals(testString));
672    }
673
674    /**
675     * @tests java.io.OutputStreamWriter#write(int)
676     */
677    public void test_writeI() throws IOException {
678        osw.write('T');
679        osw.close();
680        openInputStream();
681        int c = isr.read();
682        assertEquals("Incorrect char returned", 'T', (char) c);
683    }
684
685    /**
686     * @tests java.io.OutputStreamWriter#write(java.lang.String, int, int)
687     */
688    public void test_writeLjava_lang_StringII() throws IOException {
689        char[] buf = new char[testString.length()];
690        osw.write(testString, 0, testString.length());
691        osw.close();
692        openInputStream();
693        isr.read(buf);
694        assertTrue("Incorrect chars returned", new String(buf, 0, buf.length)
695                .equals(testString));
696    }
697
698    private void openInputStream() {
699        isr = new InputStreamReader(new ByteArrayInputStream(fos.toByteArray()));
700    }
701}
702