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  
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.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  
24  import java.io.ByteArrayOutputStream;
25  import java.io.OutputStream;
26  import java.util.Arrays;
27  
28  import org.junit.jupiter.api.Test;
29  
30  /**
31   * Tests {@link Base45OutputStream}.
32   */
33  class Base45OutputStreamTest extends AbstractBaseNOutputStreamTest {
34  
35      private static final byte[] CR_LF = { (byte) '\r', (byte) '\n' };
36      private static final byte[] LF = { (byte) '\n' };
37  
38      @Override
39      OutputStream newOutputStream() {
40          return new Base45OutputStream(new ByteArrayOutputStream());
41      }
42  
43      private void testBase45EmptyOutputStream(final int chunkSize) throws Exception {
44          final byte[] emptyEncoded = {};
45          final byte[] emptyDecoded = {};
46          testByteByByte(emptyEncoded, emptyDecoded, chunkSize, CR_LF);
47          testByChunk(emptyEncoded, emptyDecoded, chunkSize, CR_LF);
48      }
49  
50      @Test
51      void testBase45EmptyOutputStreamMimeChunkSize() throws Exception {
52          testBase45EmptyOutputStream(BaseNCodec.MIME_CHUNK_SIZE);
53      }
54  
55      @Test
56      void testBase45EmptyOutputStreamPemChunkSize() throws Exception {
57          testBase45EmptyOutputStream(BaseNCodec.PEM_CHUNK_SIZE);
58      }
59  
60      @Test
61      void testBase45OutputStreamByChunk() throws Exception {
62          byte[] decoded = StringUtils.getBytesUtf8("Hello World");
63          byte[] encoded = new Base45().encode(decoded);
64          testByChunk(encoded, decoded, BaseNCodec.MIME_CHUNK_SIZE, CR_LF);
65          final BaseNCodec codec = new Base45();
66          for (int i = 0; i <= 150; i++) {
67              final byte[][] randomData = BaseNTestData.randomData(codec, i);
68              encoded = randomData[1];
69              decoded = randomData[0];
70              testByChunk(encoded, decoded, 0, LF);
71          }
72      }
73  
74      @Test
75      void testBase45OutputStreamByteByByte() throws Exception {
76          byte[] decoded = StringUtils.getBytesUtf8("Hello World");
77          byte[] encoded = new Base45().encode(decoded);
78          testByteByByte(encoded, decoded, 76, CR_LF);
79          final BaseNCodec codec = new Base45();
80          for (int i = 0; i <= 150; i++) {
81              final byte[][] randomData = BaseNTestData.randomData(codec, i);
82              encoded = randomData[1];
83              decoded = randomData[0];
84              testByteByByte(encoded, decoded, 0, LF);
85          }
86      }
87  
88      @Test
89      void testBuilder() {
90          assertNotNull(Base45OutputStream.builder().getBaseNCodec());
91      }
92  
93      private void testByChunk(final byte[] encoded, final byte[] decoded, final int chunkSize, final byte[] separator) throws Exception {
94          ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
95          try (OutputStream out = Base45OutputStream.builder().setOutputStream(byteOut).setEncode(true).get()) {
96              out.write(decoded);
97          }
98          byte[] output = byteOut.toByteArray();
99          assertArrayEquals(encoded, output, "Streaming chunked Base45 encode");
100         byteOut = new ByteArrayOutputStream();
101         try (OutputStream out = Base45OutputStream.builder().setOutputStream(byteOut).setEncode(false).get()) {
102             out.write(encoded);
103         }
104         output = byteOut.toByteArray();
105         assertArrayEquals(decoded, output, "Streaming chunked Base45 decode");
106         byteOut = new ByteArrayOutputStream();
107         OutputStream out = byteOut;
108         for (int i = 0; i < 10; i++) {
109             out = Base45OutputStream.builder().setOutputStream(out).setEncode(false).get();
110             out = Base45OutputStream.builder().setOutputStream(out).setEncode(true).get();
111         }
112         out.write(decoded);
113         out.close();
114         output = byteOut.toByteArray();
115         assertArrayEquals(decoded, byteOut.toByteArray(), "Streaming chunked Base45 wrap-wrap-wrap!");
116     }
117 
118     private void testByteByByte(final byte[] encoded, final byte[] decoded, final int chunkSize, final byte[] separator) throws Exception {
119         ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
120         try (OutputStream out = Base45OutputStream.builder().setOutputStream(byteOut).setEncode(true).get()) {
121             for (final byte element : decoded) {
122                 out.write(element);
123             }
124         }
125         final byte[] output0 = byteOut.toByteArray();
126         assertArrayEquals(encoded, output0, "Streaming byte-by-byte Base45 encode");
127         byteOut = new ByteArrayOutputStream();
128         try (OutputStream out = Base45OutputStream.builder().setOutputStream(byteOut).setEncode(false).get()) {
129             for (final byte element : encoded) {
130                 out.write(element);
131             }
132         }
133         final byte[] output1 = byteOut.toByteArray();
134         assertArrayEquals(decoded, output1,
135                 () -> String.format("Streaming byte-by-byte Base45 decode, chunkSize=%d, separator=%s, encoded=%s, decoded=%s, output=%s", chunkSize,
136                         Arrays.toString(separator), Arrays.toString(encoded), Arrays.toString(decoded), Arrays.toString(output1)));
137         byteOut = new ByteArrayOutputStream();
138         try (OutputStream out = Base45OutputStream.builder().setOutputStream(byteOut).setEncode(false).get()) {
139             for (final byte element : encoded) {
140                 out.write(element);
141                 out.flush();
142             }
143         }
144         byte[] output = byteOut.toByteArray();
145         assertArrayEquals(decoded, output, "Streaming byte-by-byte flush() Base45 decode");
146         byteOut = new ByteArrayOutputStream();
147         OutputStream out = byteOut;
148         for (int i = 0; i < 10; i++) {
149             out = Base45OutputStream.builder().setOutputStream(out).setEncode(false).get();
150             out = Base45OutputStream.builder().setOutputStream(out).setEncode(true).get();
151         }
152         for (final byte element : decoded) {
153             out.write(element);
154         }
155         out.close();
156         output = byteOut.toByteArray();
157         assertArrayEquals(decoded, output, "Streaming byte-by-byte Base45 wrap-wrap-wrap!");
158     }
159 
160     @Test
161     void testWriteOutOfBounds() throws Exception {
162         final byte[] buf = new byte[1024];
163         final ByteArrayOutputStream bout = new ByteArrayOutputStream();
164         try (Base45OutputStream out = new Base45OutputStream(bout)) {
165             assertThrows(IndexOutOfBoundsException.class, () -> out.write(buf, -1, 1), "Base45OutputStream.write(buf, -1, 1)");
166             assertThrows(IndexOutOfBoundsException.class, () -> out.write(buf, 1, -1), "Base45OutputStream.write(buf, 1, -1)");
167             assertThrows(IndexOutOfBoundsException.class, () -> out.write(buf, buf.length + 1, 0), "Base45OutputStream.write(buf, buf.length + 1, 0)");
168             assertThrows(IndexOutOfBoundsException.class, () -> out.write(buf, buf.length - 1, 2), "Base45OutputStream.write(buf, buf.length - 1, 2)");
169         }
170     }
171 
172     @Test
173     void testWriteToNullCoverage() throws Exception {
174         final ByteArrayOutputStream bout = new ByteArrayOutputStream();
175         try (Base45OutputStream out = new Base45OutputStream(bout)) {
176             assertThrows(NullPointerException.class, () -> out.write(null, 0, 0));
177         }
178     }
179 }