View Javadoc
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    *      https://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  package org.apache.commons.io.output;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.fail;
21  
22  import java.io.IOException;
23  import java.io.StringWriter;
24  import java.nio.charset.Charset;
25  import java.nio.charset.CharsetDecoder;
26  import java.nio.charset.StandardCharsets;
27  import java.nio.file.StandardOpenOption;
28  import java.util.Random;
29  
30  import org.apache.commons.io.Charsets;
31  import org.apache.commons.io.charset.CharsetDecoders;
32  import org.apache.commons.lang3.StringUtils;
33  import org.apache.commons.lang3.SystemProperties;
34  import org.junit.jupiter.api.Test;
35  
36  /**
37   * Tests {@link WriterOutputStream}.
38   */
39  class WriterOutputStreamTest {
40  
41      private static final String UTF_16LE = StandardCharsets.UTF_16LE.name();
42      private static final String UTF_16BE = StandardCharsets.UTF_16BE.name();
43      private static final String UTF_16 = StandardCharsets.UTF_16.name();
44      private static final String UTF_8 = StandardCharsets.UTF_8.name();
45      private static final String TEST_STRING = "\u00e0 peine arriv\u00e9s nous entr\u00e2mes dans sa chambre";
46      private static final String LARGE_TEST_STRING = StringUtils.repeat(TEST_STRING, 100);
47  
48      private final Random random = new Random();
49  
50      @Test
51      void testFlush() throws IOException {
52          final StringWriter writer = new StringWriter();
53          try (WriterOutputStream out = new WriterOutputStream(writer, "us-ascii", 1024, false)) {
54              out.write("abc".getBytes(StandardCharsets.US_ASCII));
55              assertEquals(0, writer.getBuffer().length());
56              out.flush();
57              assertEquals("abc", writer.toString());
58          }
59      }
60  
61      @Test
62      void testLargeUTF8CharsetWithBufferedWrite() throws IOException {
63          testWithBufferedWrite(LARGE_TEST_STRING, UTF_8);
64      }
65  
66      @Test
67      void testLargeUTF8CharsetWithSingleByteWrite() throws IOException {
68          testWithSingleByteWrite(LARGE_TEST_STRING, StandardCharsets.UTF_8);
69      }
70  
71      @Test
72      void testLargeUTF8WithBufferedWrite() throws IOException {
73          testWithBufferedWrite(LARGE_TEST_STRING, UTF_8);
74      }
75  
76      @Test
77      void testLargeUTF8WithSingleByteWrite() throws IOException {
78          testWithSingleByteWrite(LARGE_TEST_STRING, UTF_8);
79      }
80  
81      @Test
82      void testNullCharsetDecoderWithSingleByteWrite() throws IOException {
83          testWithSingleByteWrite(TEST_STRING, (CharsetDecoder) null);
84      }
85  
86      @Test
87      void testNullCharsetNameWithSingleByteWrite() throws IOException {
88          testWithSingleByteWrite(TEST_STRING, (String) null);
89      }
90  
91      @Test
92      void testNullCharsetWithSingleByteWrite() throws IOException {
93          testWithSingleByteWrite(TEST_STRING, (Charset) null);
94      }
95  
96      @Test
97      void testUTF16BEWithBufferedWrite() throws IOException {
98          testWithBufferedWrite(TEST_STRING, UTF_16BE);
99      }
100 
101     @Test
102     void testUTF16BEWithSingleByteWrite() throws IOException {
103         testWithSingleByteWrite(TEST_STRING, UTF_16BE);
104     }
105 
106     @Test
107     void testUTF16LEWithBufferedWrite() throws IOException {
108         testWithBufferedWrite(TEST_STRING, UTF_16LE);
109     }
110 
111     @Test
112     void testUTF16LEWithSingleByteWrite() throws IOException {
113         testWithSingleByteWrite(TEST_STRING, UTF_16LE);
114     }
115 
116     @Test
117     void testUTF16WithBufferedWrite() throws IOException {
118         try {
119             testWithBufferedWrite(TEST_STRING, UTF_16);
120         } catch (final UnsupportedOperationException e) {
121             if (!SystemProperties.getJavaVendor().contains("IBM")) {
122                 fail("This test should only throw UOE on IBM JDKs with broken UTF-16");
123             }
124         }
125     }
126 
127     @Test
128     void testUTF16WithSingleByteWrite() throws IOException {
129         try {
130             testWithSingleByteWrite(TEST_STRING, UTF_16);
131         } catch (final UnsupportedOperationException e) {
132             if (!SystemProperties.getJavaVendor().contains("IBM")) {
133                 fail("This test should only throw UOE on IBM JDKs with broken UTF-16");
134             }
135         }
136     }
137 
138     @Test
139     void testUTF8WithBufferedWrite() throws IOException {
140         testWithBufferedWrite(TEST_STRING, UTF_8);
141     }
142 
143     @Test
144     void testUTF8WithSingleByteWrite() throws IOException {
145         testWithSingleByteWrite(TEST_STRING, UTF_8);
146     }
147 
148     private void testWithBufferedWrite(final String testString, final String charsetName) throws IOException {
149         final byte[] expected = testString.getBytes(charsetName);
150         final StringWriter writer = new StringWriter();
151         try (WriterOutputStream out = WriterOutputStream.builder().setWriter(writer).setCharset(charsetName).get()) {
152             int offset = 0;
153             while (offset < expected.length) {
154                 final int length = Math.min(random.nextInt(128), expected.length - offset);
155                 out.write(expected, offset, length);
156                 offset += length;
157             }
158         }
159         assertEquals(testString, writer.toString());
160     }
161 
162     private void testWithSingleByteWrite(final String testString, final Charset charset) throws IOException {
163         final byte[] bytes = testString.getBytes(Charsets.toCharset(charset));
164         StringWriter writer = new StringWriter();
165         try (WriterOutputStream out = new WriterOutputStream(writer, charset)) {
166             writeOneAtATime(bytes, out);
167         }
168         assertEquals(testString, writer.toString());
169         //
170         writer = new StringWriter();
171         try (WriterOutputStream out = WriterOutputStream.builder().setWriter(writer).setCharset(charset).get()) {
172             writeOneAtATime(bytes, out);
173         }
174         assertEquals(testString, writer.toString());
175     }
176 
177     private void testWithSingleByteWrite(final String testString, final CharsetDecoder charsetDecoder) throws IOException {
178         final byte[] bytes = testString.getBytes(CharsetDecoders.toCharsetDecoder(charsetDecoder).charset());
179         StringWriter writer = new StringWriter();
180         try (WriterOutputStream out = new WriterOutputStream(writer, charsetDecoder)) {
181             writeOneAtATime(bytes, out);
182         }
183         assertEquals(testString, writer.toString());
184         //
185         writer = new StringWriter();
186         try (WriterOutputStream out = WriterOutputStream.builder().setWriter(writer).setCharsetDecoder(charsetDecoder).get()) {
187             writeOneAtATime(bytes, out);
188         }
189         assertEquals(testString, writer.toString());
190     }
191 
192     private void testWithSingleByteWrite(final String testString, final String charsetName) throws IOException {
193         final byte[] bytes = testString.getBytes(Charsets.toCharset(charsetName));
194         StringWriter writer = new StringWriter();
195         try (WriterOutputStream out = new WriterOutputStream(writer, charsetName)) {
196             writeOneAtATime(bytes, out);
197         }
198         assertEquals(testString, writer.toString());
199         //
200         writer = new StringWriter();
201         try (WriterOutputStream out = WriterOutputStream.builder().setWriter(writer).setCharset(charsetName).get()) {
202             writeOneAtATime(bytes, out);
203         }
204         assertEquals(testString, writer.toString());
205     }
206 
207     @Test
208     void testWriteImmediately() throws IOException {
209         final StringWriter writer = new StringWriter();
210         try (WriterOutputStream out = new WriterOutputStream(writer, "us-ascii", 1024, true)) {
211             out.write("abc".getBytes(StandardCharsets.US_ASCII));
212             assertEquals("abc", writer.toString());
213         }
214         // @formatter:off
215         try (WriterOutputStream out = WriterOutputStream.builder()
216                 .setWriter(writer)
217                 .setCharset("us-ascii")
218                 .setBufferSize(1024)
219                 .setWriteImmediately(true)
220                 .setOpenOptions(StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE)
221                 .get()) {
222             // @formatter:on
223             out.write("abc".getBytes(StandardCharsets.US_ASCII));
224             assertEquals("abcabc", writer.toString());
225         }
226     }
227 
228     private void writeOneAtATime(final byte[] bytes, final WriterOutputStream out) throws IOException {
229         for (final byte b : bytes) {
230             out.write(b);
231         }
232     }
233 }