1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertFalse;
23 import static org.junit.jupiter.api.Assertions.assertNotNull;
24 import static org.junit.jupiter.api.Assertions.assertThrows;
25 import static org.junit.jupiter.api.Assertions.assertTrue;
26
27 import java.io.ByteArrayOutputStream;
28 import java.io.OutputStream;
29
30 import org.apache.commons.codec.CodecPolicy;
31 import org.junit.jupiter.api.Test;
32
33
34
35
36 class Base64OutputStreamTest {
37
38 private static final byte[] CR_LF = {(byte) '\r', (byte) '\n'};
39
40 private static final byte[] LF = {(byte) '\n'};
41
42 private static final String STRING_FIXTURE = "Hello World";
43
44 private void testBase64EmptyOutputStream(final int chunkSize) throws Exception {
45 final byte[] emptyEncoded = {};
46 final byte[] emptyDecoded = {};
47 testByteByByte(emptyEncoded, emptyDecoded, chunkSize, CR_LF);
48 testByChunk(emptyEncoded, emptyDecoded, chunkSize, CR_LF);
49 }
50
51
52
53
54
55
56
57 @Test
58 void testBase64EmptyOutputStreamMimeChunkSize() throws Exception {
59 testBase64EmptyOutputStream(BaseNCodec.MIME_CHUNK_SIZE);
60 }
61
62
63
64
65
66
67
68 @Test
69 void testBase64EmptyOutputStreamPemChunkSize() throws Exception {
70 testBase64EmptyOutputStream(BaseNCodec.PEM_CHUNK_SIZE);
71 }
72
73
74
75
76
77
78
79 @Test
80 void testBase64OutputStreamByChunk() throws Exception {
81
82 byte[] encoded = StringUtils.getBytesUtf8("SGVsbG8gV29ybGQ=\r\n");
83 byte[] decoded = StringUtils.getBytesUtf8(STRING_FIXTURE);
84 testByChunk(encoded, decoded, BaseNCodec.MIME_CHUNK_SIZE, CR_LF);
85
86
87 encoded = StringUtils.getBytesUtf8("AA==\r\n");
88 decoded = new byte[]{(byte) 0};
89 testByChunk(encoded, decoded, BaseNCodec.MIME_CHUNK_SIZE, CR_LF);
90
91
92 encoded = StringUtils.getBytesUtf8(Base64TestData.ENCODED_64_CHARS_PER_LINE);
93 decoded = BaseNTestData.DECODED;
94 testByChunk(encoded, decoded, BaseNCodec.PEM_CHUNK_SIZE, LF);
95
96
97 final String singleLine = Base64TestData.ENCODED_64_CHARS_PER_LINE.replace("\n", "");
98 encoded = StringUtils.getBytesUtf8(singleLine);
99 decoded = BaseNTestData.DECODED;
100 testByChunk(encoded, decoded, 0, LF);
101
102
103 final BaseNCodec codec = new Base64(0, null, false);
104 for (int i = 0; i <= 150; i++) {
105 final byte[][] randomData = BaseNTestData.randomData(codec, i);
106 encoded = randomData[1];
107 decoded = randomData[0];
108 testByChunk(encoded, decoded, 0, LF);
109 }
110 }
111
112
113
114
115
116
117
118 @Test
119 void testBase64OutputStreamByteByByte() throws Exception {
120
121 byte[] encoded = StringUtils.getBytesUtf8("SGVsbG8gV29ybGQ=\r\n");
122 byte[] decoded = StringUtils.getBytesUtf8(STRING_FIXTURE);
123 testByteByByte(encoded, decoded, 76, CR_LF);
124
125
126 encoded = StringUtils.getBytesUtf8("AA==\r\n");
127 decoded = new byte[]{(byte) 0};
128 testByteByByte(encoded, decoded, 76, CR_LF);
129
130
131 encoded = StringUtils.getBytesUtf8(Base64TestData.ENCODED_64_CHARS_PER_LINE);
132 decoded = BaseNTestData.DECODED;
133 testByteByByte(encoded, decoded, 64, LF);
134
135
136 final String singleLine = Base64TestData.ENCODED_64_CHARS_PER_LINE.replace("\n", "");
137 encoded = StringUtils.getBytesUtf8(singleLine);
138 decoded = BaseNTestData.DECODED;
139 testByteByByte(encoded, decoded, 0, LF);
140
141
142 final BaseNCodec codec = new Base64(0, null, false);
143 for (int i = 0; i <= 150; i++) {
144 final byte[][] randomData = BaseNTestData.randomData(codec, i);
145 encoded = randomData[1];
146 decoded = randomData[0];
147 testByteByByte(encoded, decoded, 0, LF);
148 }
149 }
150
151 @Test
152 void testBuilder() {
153 assertNotNull(Base64OutputStream.builder().getBaseNCodec());
154 }
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174 private void testByChunk(final byte[] encoded, final byte[] decoded, final int chunkSize, final byte[] separator) throws Exception {
175
176
177 ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
178 try (OutputStream out = new Base64OutputStream(byteOut, true, chunkSize, separator)) {
179 out.write(decoded);
180 }
181 byte[] output = byteOut.toByteArray();
182 assertArrayEquals(encoded, output, "Streaming chunked base64 encode");
183
184
185 byteOut = new ByteArrayOutputStream();
186 try (OutputStream out = new Base64OutputStream(byteOut, false)) {
187 out.write(encoded);
188 }
189 output = byteOut.toByteArray();
190 assertArrayEquals(decoded, output, "Streaming chunked base64 decode");
191
192
193 byteOut = new ByteArrayOutputStream();
194 OutputStream out = byteOut;
195 for (int i = 0; i < 10; i++) {
196 out = new Base64OutputStream(out, false);
197 out = new Base64OutputStream(out, true, chunkSize, separator);
198 }
199 out.write(decoded);
200 out.close();
201 output = byteOut.toByteArray();
202
203 assertArrayEquals(decoded, output, "Streaming chunked base64 wrap-wrap-wrap!");
204 }
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224 private void testByteByByte(final byte[] encoded, final byte[] decoded, final int chunkSize, final byte[] separator) throws Exception {
225
226
227 ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
228 try (OutputStream out = new Base64OutputStream(byteOut, true, chunkSize, separator)) {
229 for (final byte element : decoded) {
230 out.write(element);
231 }
232 }
233 byte[] output = byteOut.toByteArray();
234 assertArrayEquals(encoded, output, "Streaming byte-by-byte base64 encode");
235
236
237 byteOut = new ByteArrayOutputStream();
238 try (OutputStream out = new Base64OutputStream(byteOut, false)) {
239 for (final byte element : encoded) {
240 out.write(element);
241 }
242 }
243 output = byteOut.toByteArray();
244 assertArrayEquals(decoded, output, "Streaming byte-by-byte base64 decode");
245
246
247 byteOut = new ByteArrayOutputStream();
248 try (OutputStream out = new Base64OutputStream(byteOut, false)) {
249 for (final byte element : encoded) {
250 out.write(element);
251 out.flush();
252 }
253 }
254 output = byteOut.toByteArray();
255 assertArrayEquals(decoded, output, "Streaming byte-by-byte flush() base64 decode");
256
257
258 byteOut = new ByteArrayOutputStream();
259 OutputStream out = byteOut;
260 for (int i = 0; i < 10; i++) {
261 out = new Base64OutputStream(out, false);
262 out = new Base64OutputStream(out, true, chunkSize, separator);
263 }
264 for (final byte element : decoded) {
265 out.write(element);
266 }
267 out.close();
268 output = byteOut.toByteArray();
269
270 assertArrayEquals(decoded, output, "Streaming byte-by-byte base64 wrap-wrap-wrap!");
271 }
272
273
274
275
276
277
278
279 @Test
280 void testCodec98NPE() throws Exception {
281 final byte[] codec98 = StringUtils.getBytesUtf8(Base64TestData.CODEC_98_NPE);
282 final byte[] codec98_1024 = new byte[1024];
283 System.arraycopy(codec98, 0, codec98_1024, 0, codec98.length);
284 final ByteArrayOutputStream data = new ByteArrayOutputStream(1024);
285 try (Base64OutputStream stream = new Base64OutputStream(data, false)) {
286 stream.write(codec98_1024, 0, 1024);
287 }
288
289 final byte[] decodedBytes = data.toByteArray();
290 final String decoded = StringUtils.newStringUtf8(decodedBytes);
291 assertEquals(Base64TestData.CODEC_98_NPE_DECODED, decoded, "codec-98 NPE Base64OutputStream");
292 }
293
294
295
296
297
298
299
300 @Test
301 void testStrictDecoding() throws Exception {
302 for (final String impossibleStr : Base64Test.BASE64_IMPOSSIBLE_CASES) {
303 final byte[] impossibleEncoded = StringUtils.getBytesUtf8(impossibleStr);
304 ByteArrayOutputStream bout = new ByteArrayOutputStream();
305 try (Base64OutputStream out = new Base64OutputStream(bout, false)) {
306
307 assertFalse(out.isStrictDecoding());
308 out.write(impossibleEncoded);
309 }
310 assertTrue(bout.size() > 0);
311
312 bout = new ByteArrayOutputStream();
313 try (Base64OutputStream out = new Base64OutputStream(bout, false, 0, null, CodecPolicy.STRICT)) {
314
315
316 assertThrows(IllegalArgumentException.class, () -> {
317 out.write(impossibleEncoded);
318 out.close();
319 });
320 }
321 try (Base64OutputStream out = Base64OutputStream.builder()
322 .setOutputStream(bout).setEncode(false)
323 .setBaseNCodec(Base64.builder().setLineLength(0).setLineSeparator(null).setDecodingPolicy(CodecPolicy.STRICT).get())
324 .get()) {
325 assertTrue(out.isStrictDecoding());
326 assertThrows(IllegalArgumentException.class, () -> {
327 out.write(impossibleEncoded);
328 out.close();
329 });
330 }
331 try (Base64OutputStream out = Base64OutputStream.builder()
332 .setOutputStream(bout).setEncode(false)
333 .setBaseNCodec(Base64.builder().setDecodingPolicy(CodecPolicy.STRICT).get())
334 .get()) {
335
336
337 assertThrows(IllegalArgumentException.class, () -> {
338 out.write(impossibleEncoded);
339 out.close();
340 });
341 }
342 }
343 }
344
345
346
347
348
349
350
351 @Test
352 void testWriteOutOfBounds() throws Exception {
353 final byte[] buf = new byte[1024];
354 final ByteArrayOutputStream bout = new ByteArrayOutputStream();
355 try (Base64OutputStream out = new Base64OutputStream(bout)) {
356 assertThrows(IndexOutOfBoundsException.class, () -> out.write(buf, -1, 1), "Base64OutputStream.write(buf, -1, 1)");
357 assertThrows(IndexOutOfBoundsException.class, () -> out.write(buf, 1, -1), "Base64OutputStream.write(buf, 1, -1)");
358 assertThrows(IndexOutOfBoundsException.class, () -> out.write(buf, buf.length + 1, 0), "Base64OutputStream.write(buf, buf.length + 1, 0)");
359 assertThrows(IndexOutOfBoundsException.class, () -> out.write(buf, buf.length - 1, 2), "Base64OutputStream.write(buf, buf.length - 1, 2)");
360 }
361 }
362
363
364
365
366
367
368
369 @Test
370 void testWriteToNullCoverage() throws Exception {
371 final ByteArrayOutputStream bout = new ByteArrayOutputStream();
372 try (Base64OutputStream out = new Base64OutputStream(bout)) {
373 assertThrows(NullPointerException.class, () -> out.write(null, 0, 0));
374 }
375 }
376 }