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    *      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  
18  package org.apache.commons.codec.binary;
19  
20  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.io.ByteArrayOutputStream;
26  import java.io.OutputStream;
27  
28  import org.apache.commons.codec.CodecPolicy;
29  import org.junit.jupiter.api.Test;
30  
31  public class Base32OutputStreamTest {
32  
33      private final static byte[] CR_LF = {(byte) '\r', (byte) '\n'};
34  
35      private final static byte[] LF = {(byte) '\n'};
36  
37  //    /**
38  //     * Test the Base32OutputStream implementation against the special NPE inducing input
39  //     * identified in the CODEC-98 bug.
40  //     *
41  //     * @throws Exception for some failure scenarios.
42  //     */
43  //    @Test
44  //    public void testCodec98NPE() throws Exception {
45  //        byte[] codec98 = StringUtils.getBytesUtf8(Base32TestData.CODEC_98_NPE);
46  //        byte[] codec98_1024 = new byte[1024];
47  //        System.arraycopy(codec98, 0, codec98_1024, 0, codec98.length);
48  //        ByteArrayOutputStream data = new ByteArrayOutputStream(1024);
49  //        Base32OutputStream stream = new Base32OutputStream(data, false);
50  //        stream.write(codec98_1024, 0, 1024);
51  //        stream.close();
52  //
53  //        byte[] decodedBytes = data.toByteArray();
54  //        String decoded = StringUtils.newStringUtf8(decodedBytes);
55  //        assertEquals(
56  //            "codec-98 NPE Base32OutputStream", Base32TestData.CODEC_98_NPE_DECODED, decoded
57  //        );
58  //    }
59  
60      private void testBase32EmptyOutputStream(final int chunkSize) throws Exception {
61          final byte[] emptyEncoded = {};
62          final byte[] emptyDecoded = {};
63          testByteByByte(emptyEncoded, emptyDecoded, chunkSize, CR_LF);
64          testByChunk(emptyEncoded, emptyDecoded, chunkSize, CR_LF);
65      }
66  
67      /**
68       * Test the Base32OutputStream implementation against empty input.
69       *
70       * @throws Exception
71       *             for some failure scenarios.
72       */
73      @Test
74      public void testBase32EmptyOutputStreamMimeChunkSize() throws Exception {
75          testBase32EmptyOutputStream(BaseNCodec.MIME_CHUNK_SIZE);
76      }
77  
78      /**
79       * Test the Base32OutputStream implementation against empty input.
80       *
81       * @throws Exception
82       *             for some failure scenarios.
83       */
84      @Test
85      public void testBase32EmptyOutputStreamPemChunkSize() throws Exception {
86          testBase32EmptyOutputStream(BaseNCodec.PEM_CHUNK_SIZE);
87      }
88  
89      /**
90       * Test the Base32OutputStream implementation
91       *
92       * @throws Exception
93       *             for some failure scenarios.
94       */
95      @Test
96      public void testBase32OutputStreamByChunk() throws Exception {
97          // Hello World test.
98          byte[] encoded = StringUtils.getBytesUtf8(Base32TestData.BASE32_FIXTURE);
99          byte[] decoded = StringUtils.getBytesUtf8(Base32TestData.STRING_FIXTURE);
100         testByChunk(encoded, decoded, BaseNCodec.MIME_CHUNK_SIZE, CR_LF);
101 
102 //        // Single Byte test.
103 //        encoded = StringUtils.getBytesUtf8("AA==\r\n");
104 //        decoded = new byte[]{(byte) 0};
105 //        testByChunk(encoded, decoded, Base32.MIME_CHUNK_SIZE, CRLF);
106 
107 //        // Single Line test.
108 //        String singleLine = Base32TestData.ENCODED_64_CHARS_PER_LINE.replaceAll("\n", "");
109 //        encoded = StringUtils.getBytesUtf8(singleLine);
110 //        decoded = Base32TestData.DECODED;
111 //        testByChunk(encoded, decoded, 0, LF);
112 
113         // test random data of sizes 0 through 150
114         final BaseNCodec codec = new Base32();
115         for (int i = 0; i <= 150; i++) {
116             final byte[][] randomData = BaseNTestData.randomData(codec, i);
117             encoded = randomData[1];
118             decoded = randomData[0];
119             testByChunk(encoded, decoded, 0, LF);
120         }
121     }
122 
123     /**
124      * Test the Base32OutputStream implementation
125      *
126      * @throws Exception
127      *             for some failure scenarios.
128      */
129     @Test
130     public void testBase32OutputStreamByteByByte() throws Exception {
131         // Hello World test.
132         byte[] encoded = StringUtils.getBytesUtf8(Base32TestData.BASE32_FIXTURE);
133         byte[] decoded = StringUtils.getBytesUtf8(Base32TestData.STRING_FIXTURE);
134         testByteByByte(encoded, decoded, 76, CR_LF);
135 
136 //        // Single Byte test.
137 //        encoded = StringUtils.getBytesUtf8("AA==\r\n");
138 //        decoded = new byte[]{(byte) 0};
139 //        testByteByByte(encoded, decoded, 76, CRLF);
140 
141 //        // Single Line test.
142 //        String singleLine = Base32TestData.ENCODED_64_CHARS_PER_LINE.replaceAll("\n", "");
143 //        encoded = StringUtils.getBytesUtf8(singleLine);
144 //        decoded = Base32TestData.DECODED;
145 //        testByteByByte(encoded, decoded, 0, LF);
146 
147         // test random data of sizes 0 through 150
148         final BaseNCodec codec = new Base32();
149         for (int i = 0; i <= 150; i++) {
150             final byte[][] randomData = BaseNTestData.randomData(codec, i);
151             encoded = randomData[1];
152             decoded = randomData[0];
153             testByteByByte(encoded, decoded, 0, LF);
154         }
155     }
156 
157     /**
158      * Test method does three tests on the supplied data: 1. encoded ---[DECODE]--> decoded 2. decoded ---[ENCODE]-->
159      * encoded 3. decoded ---[WRAP-WRAP-WRAP-etc...] --> decoded
160      * <p/>
161      * By "[WRAP-WRAP-WRAP-etc...]" we mean situation where the Base32OutputStream wraps itself in encode and decode
162      * mode over and over again.
163      *
164      * @param encoded
165      *            Base32 encoded data
166      * @param decoded
167      *            the data from above, but decoded
168      * @param chunkSize
169      *            chunk size (line-length) of the Base32 encoded data.
170      * @param separator
171      *            Line separator in the Base32 encoded data.
172      * @throws Exception
173      *             Usually signifies a bug in the Base32 commons-codec implementation.
174      */
175     private void testByChunk(final byte[] encoded, final byte[] decoded, final int chunkSize, final byte[] separator) throws Exception {
176 
177         // Start with encode.
178         ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
179         try (OutputStream out = new Base32OutputStream(byteOut, true, chunkSize, separator)) {
180             out.write(decoded);
181         }
182         byte[] output = byteOut.toByteArray();
183         assertArrayEquals(encoded, output, "Streaming chunked Base32 encode");
184 
185         // Now let's try to decode.
186         byteOut = new ByteArrayOutputStream();
187         try (OutputStream out = new Base32OutputStream(byteOut, false)) {
188             out.write(encoded);
189         }
190         output = byteOut.toByteArray();
191         assertArrayEquals(decoded, output, "Streaming chunked Base32 decode");
192 
193         // I always wanted to do this! (wrap encoder with decoder etc.).
194         byteOut = new ByteArrayOutputStream();
195         OutputStream out = byteOut;
196         for (int i = 0; i < 10; i++) {
197             out = new Base32OutputStream(out, false);
198             out = new Base32OutputStream(out, true, chunkSize, separator);
199         }
200         out.write(decoded);
201         out.close();
202         output = byteOut.toByteArray();
203 
204         assertArrayEquals(decoded, byteOut.toByteArray(), "Streaming chunked Base32 wrap-wrap-wrap!");
205     }
206 
207     /**
208      * Test method does three tests on the supplied data: 1. encoded ---[DECODE]--> decoded 2. decoded ---[ENCODE]-->
209      * encoded 3. decoded ---[WRAP-WRAP-WRAP-etc...] --> decoded
210      * <p/>
211      * By "[WRAP-WRAP-WRAP-etc...]" we mean situation where the Base32OutputStream wraps itself in encode and decode
212      * mode over and over again.
213      *
214      * @param encoded
215      *            Base32 encoded data
216      * @param decoded
217      *            the data from above, but decoded
218      * @param chunkSize
219      *            chunk size (line-length) of the Base32 encoded data.
220      * @param separator
221      *            Line separator in the Base32 encoded data.
222      * @throws Exception
223      *             Usually signifies a bug in the Base32 commons-codec implementation.
224      */
225     private void testByteByByte(final byte[] encoded, final byte[] decoded, final int chunkSize, final byte[] separator) throws Exception {
226 
227         // Start with encode.
228         ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
229         try (OutputStream out = new Base32OutputStream(byteOut, true, chunkSize, separator)) {
230             for (final byte element : decoded) {
231                 out.write(element);
232             }
233         }
234         byte[] output = byteOut.toByteArray();
235         assertArrayEquals(encoded, output, "Streaming byte-by-byte Base32 encode");
236 
237         // Now let's try to decode.
238         byteOut = new ByteArrayOutputStream();
239         try (OutputStream out = new Base32OutputStream(byteOut, false)) {
240             for (final byte element : encoded) {
241                 out.write(element);
242             }
243         }
244         output = byteOut.toByteArray();
245         assertArrayEquals(decoded, output, "Streaming byte-by-byte Base32 decode");
246 
247         // Now let's try to decode with tonnes of flushes.
248         byteOut = new ByteArrayOutputStream();
249         try (OutputStream out = new Base32OutputStream(byteOut, false)) {
250             for (final byte element : encoded) {
251                 out.write(element);
252                 out.flush();
253             }
254         }
255         output = byteOut.toByteArray();
256         assertArrayEquals(decoded, output, "Streaming byte-by-byte flush() Base32 decode");
257 
258         // I always wanted to do this! (wrap encoder with decoder etc.).
259         byteOut = new ByteArrayOutputStream();
260         OutputStream out = byteOut;
261         for (int i = 0; i < 10; i++) {
262             out = new Base32OutputStream(out, false);
263             out = new Base32OutputStream(out, true, chunkSize, separator);
264         }
265         for (final byte element : decoded) {
266             out.write(element);
267         }
268         out.close();
269         output = byteOut.toByteArray();
270 
271         assertArrayEquals(decoded, output, "Streaming byte-by-byte Base32 wrap-wrap-wrap!");
272     }
273 
274     /**
275      * Test strict decoding.
276      *
277      * @throws Exception
278      *             for some failure scenarios.
279      */
280     @Test
281     public void testStrictDecoding() throws Exception {
282         for (final String s : Base32Test.BASE32_IMPOSSIBLE_CASES) {
283             final byte[] encoded = StringUtils.getBytesUtf8(s);
284             ByteArrayOutputStream bout = new ByteArrayOutputStream();
285             try (Base32OutputStream out = new Base32OutputStream(bout, false)) {
286                 // Default is lenient decoding; it should not throw
287                 assertFalse(out.isStrictDecoding());
288                 out.write(encoded);
289                 out.close();
290                 assertTrue(bout.size() > 0);
291 
292                 // Strict decoding should throw
293                 bout = new ByteArrayOutputStream();
294                 try (final Base32OutputStream out2 = new Base32OutputStream(bout, false, 0, null, CodecPolicy.STRICT)) {
295                     assertTrue(out2.isStrictDecoding());
296                     assertThrows(IllegalArgumentException.class, () -> out2.write(encoded));
297                 }
298             }
299         }
300     }
301 
302     /**
303      * Tests Base32OutputStream.write for expected IndexOutOfBoundsException conditions.
304      *
305      * @throws Exception
306      *             for some failure scenarios.
307      */
308     @Test
309     public void testWriteOutOfBounds() throws Exception {
310         final byte[] buf = new byte[1024];
311         final ByteArrayOutputStream bout = new ByteArrayOutputStream();
312         try (final Base32OutputStream out = new Base32OutputStream(bout)) {
313             assertThrows(IndexOutOfBoundsException.class, () -> out.write(buf, -1, 1), "Base32OutputStream.write(buf, -1, 1)");
314             assertThrows(IndexOutOfBoundsException.class, () -> out.write(buf, 1, -1), "Base32OutputStream.write(buf, 1, -1)");
315             assertThrows(IndexOutOfBoundsException.class, () -> out.write(buf, buf.length + 1, 0), "Base32OutputStream.write(buf, buf, buf.length + 1, 0)");
316             assertThrows(IndexOutOfBoundsException.class, () -> out.write(buf, buf.length - 1, 2), "Base32OutputStream.write(buf, buf, buf.length - 1, 2)");
317         }
318     }
319 
320     /**
321      * Tests Base32OutputStream.write(null).
322      *
323      * @throws Exception
324      *             for some failure scenarios.
325      */
326     @Test
327     public void testWriteToNullCoverage() throws Exception {
328         final ByteArrayOutputStream bout = new ByteArrayOutputStream();
329         try (final Base32OutputStream out = new Base32OutputStream(bout)) {
330             assertThrows(NullPointerException.class, () -> out.write(null, 0, 0));
331         }
332     }
333 }