• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.io;
16 
17 import static com.google.common.base.Charsets.UTF_8;
18 import static com.google.common.io.BaseEncoding.base16;
19 import static com.google.common.io.BaseEncoding.base32;
20 import static com.google.common.io.BaseEncoding.base32Hex;
21 import static com.google.common.io.BaseEncoding.base64;
22 import static com.google.common.io.BaseEncoding.base64Url;
23 import static com.google.common.truth.Truth.assertThat;
24 
25 import com.google.common.annotations.GwtCompatible;
26 import com.google.common.annotations.GwtIncompatible;
27 import com.google.common.base.Ascii;
28 import com.google.common.base.Joiner;
29 import com.google.common.base.Splitter;
30 import com.google.common.collect.ImmutableList;
31 import com.google.common.io.BaseEncoding.DecodingException;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.OutputStream;
35 import java.io.Reader;
36 import java.io.StringReader;
37 import java.io.StringWriter;
38 import junit.framework.TestCase;
39 import org.checkerframework.checker.nullness.qual.Nullable;
40 
41 /**
42  * Tests for {@code BaseEncoding}.
43  *
44  * @author Louis Wasserman
45  */
46 @GwtCompatible(emulated = true)
47 public class BaseEncodingTest extends TestCase {
48 
testSeparatorsExplicitly()49   public void testSeparatorsExplicitly() {
50     testEncodes(base64().withSeparator("\n", 3), "foobar", "Zm9\nvYm\nFy");
51     testEncodes(base64().withSeparator("$", 4), "foobar", "Zm9v$YmFy");
52     testEncodes(base32().withSeparator("*", 4), "foobar", "MZXW*6YTB*OI==*====");
53   }
54 
testSeparatorSameAsPadChar()55   public void testSeparatorSameAsPadChar() {
56     try {
57       base64().withSeparator("=", 3);
58       fail("Expected IllegalArgumentException");
59     } catch (IllegalArgumentException expected) {
60     }
61 
62     try {
63       base64().withPadChar('#').withSeparator("!#!", 3);
64       fail("Expected IllegalArgumentException");
65     } catch (IllegalArgumentException expected) {
66     }
67   }
68 
testAtMostOneSeparator()69   public void testAtMostOneSeparator() {
70     BaseEncoding separated = base64().withSeparator("\n", 3);
71     try {
72       separated.withSeparator("$", 4);
73       fail("Expected UnsupportedOperationException");
74     } catch (UnsupportedOperationException expected) {
75     }
76   }
77 
testBase64()78   public void testBase64() {
79     // The following test vectors are specified in RFC 4648 itself
80     testEncodingWithSeparators(base64(), "", "");
81     testEncodingWithSeparators(base64(), "f", "Zg==");
82     testEncodingWithSeparators(base64(), "fo", "Zm8=");
83     testEncodingWithSeparators(base64(), "foo", "Zm9v");
84     testEncodingWithSeparators(base64(), "foob", "Zm9vYg==");
85     testEncodingWithSeparators(base64(), "fooba", "Zm9vYmE=");
86     testEncodingWithSeparators(base64(), "foobar", "Zm9vYmFy");
87   }
88 
89   @GwtIncompatible // Reader/Writer
testBase64Streaming()90   public void testBase64Streaming() throws IOException {
91     // The following test vectors are specified in RFC 4648 itself
92     testStreamingEncodingWithSeparators(base64(), "", "");
93     testStreamingEncodingWithSeparators(base64(), "f", "Zg==");
94     testStreamingEncodingWithSeparators(base64(), "fo", "Zm8=");
95     testStreamingEncodingWithSeparators(base64(), "foo", "Zm9v");
96     testStreamingEncodingWithSeparators(base64(), "foob", "Zm9vYg==");
97     testStreamingEncodingWithSeparators(base64(), "fooba", "Zm9vYmE=");
98     testStreamingEncodingWithSeparators(base64(), "foobar", "Zm9vYmFy");
99   }
100 
testBase64LenientPadding()101   public void testBase64LenientPadding() {
102     testDecodes(base64(), "Zg", "f");
103     testDecodes(base64(), "Zg=", "f");
104     testDecodes(base64(), "Zg==", "f"); // proper padding length
105     testDecodes(base64(), "Zg===", "f");
106     testDecodes(base64(), "Zg====", "f");
107   }
108 
testBase64InvalidDecodings()109   public void testBase64InvalidDecodings() {
110     // These contain bytes not in the decodabet.
111     assertFailsToDecode(base64(), "A\u007f", "Unrecognized character: 0x7f");
112     assertFailsToDecode(base64(), "Wf2!", "Unrecognized character: !");
113     // This sentence just isn't base64() encoded.
114     assertFailsToDecode(base64(), "let's not talk of love or chains!");
115     // A 4n+1 length string is never legal base64().
116     assertFailsToDecode(base64(), "12345", "Invalid input length 5");
117     // These have a combination of invalid length, unrecognized characters and wrong padding.
118     assertFailsToDecode(base64(), "AB=C", "Unrecognized character: =");
119     assertFailsToDecode(base64(), "A=BCD", "Invalid input length 5");
120     assertFailsToDecode(base64(), "?", "Invalid input length 1");
121   }
122 
testBase64CannotUpperCase()123   public void testBase64CannotUpperCase() {
124     try {
125       base64().upperCase();
126       fail();
127     } catch (IllegalStateException expected) {
128       // success
129     }
130   }
131 
testBase64CannotLowerCase()132   public void testBase64CannotLowerCase() {
133     try {
134       base64().lowerCase();
135       fail();
136     } catch (IllegalStateException expected) {
137       // success
138     }
139   }
140 
testBase64AlternatePadding()141   public void testBase64AlternatePadding() {
142     BaseEncoding enc = base64().withPadChar('~');
143     testEncodingWithSeparators(enc, "", "");
144     testEncodingWithSeparators(enc, "f", "Zg~~");
145     testEncodingWithSeparators(enc, "fo", "Zm8~");
146     testEncodingWithSeparators(enc, "foo", "Zm9v");
147     testEncodingWithSeparators(enc, "foob", "Zm9vYg~~");
148     testEncodingWithSeparators(enc, "fooba", "Zm9vYmE~");
149     testEncodingWithSeparators(enc, "foobar", "Zm9vYmFy");
150   }
151 
152   @GwtIncompatible // Reader/Writer
testBase64StreamingAlternatePadding()153   public void testBase64StreamingAlternatePadding() throws IOException {
154     BaseEncoding enc = base64().withPadChar('~');
155     testStreamingEncodingWithSeparators(enc, "", "");
156     testStreamingEncodingWithSeparators(enc, "f", "Zg~~");
157     testStreamingEncodingWithSeparators(enc, "fo", "Zm8~");
158     testStreamingEncodingWithSeparators(enc, "foo", "Zm9v");
159     testStreamingEncodingWithSeparators(enc, "foob", "Zm9vYg~~");
160     testStreamingEncodingWithSeparators(enc, "fooba", "Zm9vYmE~");
161     testStreamingEncodingWithSeparators(enc, "foobar", "Zm9vYmFy");
162   }
163 
testBase64OmitPadding()164   public void testBase64OmitPadding() {
165     BaseEncoding enc = base64().omitPadding();
166     testEncodingWithSeparators(enc, "", "");
167     testEncodingWithSeparators(enc, "f", "Zg");
168     testEncodingWithSeparators(enc, "fo", "Zm8");
169     testEncodingWithSeparators(enc, "foo", "Zm9v");
170     testEncodingWithSeparators(enc, "foob", "Zm9vYg");
171     testEncodingWithSeparators(enc, "fooba", "Zm9vYmE");
172     testEncodingWithSeparators(enc, "foobar", "Zm9vYmFy");
173   }
174 
175   @GwtIncompatible // Reader/Writer
testBase64StreamingOmitPadding()176   public void testBase64StreamingOmitPadding() throws IOException {
177     BaseEncoding enc = base64().omitPadding();
178     testStreamingEncodingWithSeparators(enc, "", "");
179     testStreamingEncodingWithSeparators(enc, "f", "Zg");
180     testStreamingEncodingWithSeparators(enc, "fo", "Zm8");
181     testStreamingEncodingWithSeparators(enc, "foo", "Zm9v");
182     testStreamingEncodingWithSeparators(enc, "foob", "Zm9vYg");
183     testStreamingEncodingWithSeparators(enc, "fooba", "Zm9vYmE");
184     testStreamingEncodingWithSeparators(enc, "foobar", "Zm9vYmFy");
185   }
186 
testBase64Offset()187   public void testBase64Offset() {
188     testEncodesWithOffset(base64(), "foobar", 0, 6, "Zm9vYmFy");
189     testEncodesWithOffset(base64(), "foobar", 1, 5, "b29iYXI=");
190     testEncodesWithOffset(base64(), "foobar", 2, 3, "b2Jh");
191     testEncodesWithOffset(base64(), "foobar", 3, 1, "Yg==");
192     testEncodesWithOffset(base64(), "foobar", 4, 0, "");
193   }
194 
testBase64Url()195   public void testBase64Url() {
196     testDecodesByBytes(base64Url(), "_zzz", new byte[] {-1, 60, -13});
197     testDecodesByBytes(base64Url(), "-zzz", new byte[] {-5, 60, -13});
198   }
199 
testBase64UrlInvalidDecodings()200   public void testBase64UrlInvalidDecodings() {
201     assertFailsToDecode(base64Url(), "+zzz", "Unrecognized character: +");
202     assertFailsToDecode(base64Url(), "/zzz", "Unrecognized character: /");
203   }
204 
testBase32()205   public void testBase32() {
206     // The following test vectors are specified in RFC 4648 itself
207     testEncodingWithCasing(base32(), "", "");
208     testEncodingWithCasing(base32(), "f", "MY======");
209     testEncodingWithCasing(base32(), "fo", "MZXQ====");
210     testEncodingWithCasing(base32(), "foo", "MZXW6===");
211     testEncodingWithCasing(base32(), "foob", "MZXW6YQ=");
212     testEncodingWithCasing(base32(), "fooba", "MZXW6YTB");
213     testEncodingWithCasing(base32(), "foobar", "MZXW6YTBOI======");
214   }
215 
216   @GwtIncompatible // Reader/Writer
testBase32Streaming()217   public void testBase32Streaming() throws IOException {
218     // The following test vectors are specified in RFC 4648 itself
219     testStreamingEncodingWithCasing(base32(), "", "");
220     testStreamingEncodingWithCasing(base32(), "f", "MY======");
221     testStreamingEncodingWithCasing(base32(), "fo", "MZXQ====");
222     testStreamingEncodingWithCasing(base32(), "foo", "MZXW6===");
223     testStreamingEncodingWithCasing(base32(), "foob", "MZXW6YQ=");
224     testStreamingEncodingWithCasing(base32(), "fooba", "MZXW6YTB");
225     testStreamingEncodingWithCasing(base32(), "foobar", "MZXW6YTBOI======");
226   }
227 
testBase32LenientPadding()228   public void testBase32LenientPadding() {
229     testDecodes(base32(), "MZXW6", "foo");
230     testDecodes(base32(), "MZXW6=", "foo");
231     testDecodes(base32(), "MZXW6==", "foo");
232     testDecodes(base32(), "MZXW6===", "foo"); // proper padding length
233     testDecodes(base32(), "MZXW6====", "foo");
234     testDecodes(base32(), "MZXW6=====", "foo");
235   }
236 
testBase32AlternatePadding()237   public void testBase32AlternatePadding() {
238     BaseEncoding enc = base32().withPadChar('~');
239     testEncodingWithCasing(enc, "", "");
240     testEncodingWithCasing(enc, "f", "MY~~~~~~");
241     testEncodingWithCasing(enc, "fo", "MZXQ~~~~");
242     testEncodingWithCasing(enc, "foo", "MZXW6~~~");
243     testEncodingWithCasing(enc, "foob", "MZXW6YQ~");
244     testEncodingWithCasing(enc, "fooba", "MZXW6YTB");
245     testEncodingWithCasing(enc, "foobar", "MZXW6YTBOI~~~~~~");
246   }
247 
testBase32InvalidDecodings()248   public void testBase32InvalidDecodings() {
249     // These contain bytes not in the decodabet.
250     assertFailsToDecode(base32(), "A ", "Unrecognized character: 0x20");
251     assertFailsToDecode(base32(), "Wf2!", "Unrecognized character: f");
252     // This sentence just isn't base32() encoded.
253     assertFailsToDecode(base32(), "let's not talk of love or chains!");
254     // An 8n+{1,3,6} length string is never legal base32.
255     assertFailsToDecode(base32(), "A", "Invalid input length 1");
256     assertFailsToDecode(base32(), "ABC");
257     assertFailsToDecode(base32(), "ABCDEF");
258     // These have a combination of invalid length, unrecognized characters and wrong padding.
259     assertFailsToDecode(base32(), "AB=C", "Unrecognized character: =");
260     assertFailsToDecode(base32(), "A=BCDE", "Invalid input length 6");
261     assertFailsToDecode(base32(), "?", "Invalid input length 1");
262   }
263 
testBase32UpperCaseIsNoOp()264   public void testBase32UpperCaseIsNoOp() {
265     assertSame(base32(), base32().upperCase());
266   }
267 
testBase32Offset()268   public void testBase32Offset() {
269     testEncodesWithOffset(base32(), "foobar", 0, 6, "MZXW6YTBOI======");
270     testEncodesWithOffset(base32(), "foobar", 1, 5, "N5XWEYLS");
271     testEncodesWithOffset(base32(), "foobar", 2, 3, "N5RGC===");
272     testEncodesWithOffset(base32(), "foobar", 3, 1, "MI======");
273     testEncodesWithOffset(base32(), "foobar", 4, 0, "");
274   }
275 
testBase32Hex()276   public void testBase32Hex() {
277     // The following test vectors are specified in RFC 4648 itself
278     testEncodingWithCasing(base32Hex(), "", "");
279     testEncodingWithCasing(base32Hex(), "f", "CO======");
280     testEncodingWithCasing(base32Hex(), "fo", "CPNG====");
281     testEncodingWithCasing(base32Hex(), "foo", "CPNMU===");
282     testEncodingWithCasing(base32Hex(), "foob", "CPNMUOG=");
283     testEncodingWithCasing(base32Hex(), "fooba", "CPNMUOJ1");
284     testEncodingWithCasing(base32Hex(), "foobar", "CPNMUOJ1E8======");
285   }
286 
287   @GwtIncompatible // Reader/Writer
testBase32HexStreaming()288   public void testBase32HexStreaming() throws IOException {
289     // The following test vectors are specified in RFC 4648 itself
290     testStreamingEncodingWithCasing(base32Hex(), "", "");
291     testStreamingEncodingWithCasing(base32Hex(), "f", "CO======");
292     testStreamingEncodingWithCasing(base32Hex(), "fo", "CPNG====");
293     testStreamingEncodingWithCasing(base32Hex(), "foo", "CPNMU===");
294     testStreamingEncodingWithCasing(base32Hex(), "foob", "CPNMUOG=");
295     testStreamingEncodingWithCasing(base32Hex(), "fooba", "CPNMUOJ1");
296     testStreamingEncodingWithCasing(base32Hex(), "foobar", "CPNMUOJ1E8======");
297   }
298 
testBase32HexLenientPadding()299   public void testBase32HexLenientPadding() {
300     testDecodes(base32Hex(), "CPNMU", "foo");
301     testDecodes(base32Hex(), "CPNMU=", "foo");
302     testDecodes(base32Hex(), "CPNMU==", "foo");
303     testDecodes(base32Hex(), "CPNMU===", "foo"); // proper padding length
304     testDecodes(base32Hex(), "CPNMU====", "foo");
305     testDecodes(base32Hex(), "CPNMU=====", "foo");
306   }
307 
testBase32HexInvalidDecodings()308   public void testBase32HexInvalidDecodings() {
309     // These contain bytes not in the decodabet.
310     assertFailsToDecode(base32Hex(), "A\u007f", "Unrecognized character: 0x7f");
311     assertFailsToDecode(base32Hex(), "Wf2!", "Unrecognized character: W");
312     // This sentence just isn't base32 encoded.
313     assertFailsToDecode(base32Hex(), "let's not talk of love or chains!");
314     // An 8n+{1,3,6} length string is never legal base32.
315     assertFailsToDecode(base32Hex(), "A");
316     assertFailsToDecode(base32Hex(), "ABC");
317     assertFailsToDecode(base32Hex(), "ABCDEF");
318   }
319 
testBase32HexUpperCaseIsNoOp()320   public void testBase32HexUpperCaseIsNoOp() {
321     assertSame(base32Hex(), base32Hex().upperCase());
322   }
323 
testBase16()324   public void testBase16() {
325     testEncodingWithCasing(base16(), "", "");
326     testEncodingWithCasing(base16(), "f", "66");
327     testEncodingWithCasing(base16(), "fo", "666F");
328     testEncodingWithCasing(base16(), "foo", "666F6F");
329     testEncodingWithCasing(base16(), "foob", "666F6F62");
330     testEncodingWithCasing(base16(), "fooba", "666F6F6261");
331     testEncodingWithCasing(base16(), "foobar", "666F6F626172");
332   }
333 
testBase16UpperCaseIsNoOp()334   public void testBase16UpperCaseIsNoOp() {
335     assertSame(base16(), base16().upperCase());
336   }
337 
testBase16InvalidDecodings()338   public void testBase16InvalidDecodings() {
339     // These contain bytes not in the decodabet.
340     assertFailsToDecode(base16(), "\n\n", "Unrecognized character: 0xa");
341     assertFailsToDecode(base16(), "EFGH", "Unrecognized character: G");
342     // Valid base16 strings always have an even length.
343     assertFailsToDecode(base16(), "A", "Invalid input length 1");
344     assertFailsToDecode(base16(), "ABC");
345     // These have a combination of invalid length and unrecognized characters.
346     assertFailsToDecode(base16(), "?", "Invalid input length 1");
347   }
348 
testBase16Offset()349   public void testBase16Offset() {
350     testEncodesWithOffset(base16(), "foobar", 0, 6, "666F6F626172");
351     testEncodesWithOffset(base16(), "foobar", 1, 5, "6F6F626172");
352     testEncodesWithOffset(base16(), "foobar", 2, 3, "6F6261");
353     testEncodesWithOffset(base16(), "foobar", 3, 1, "62");
354     testEncodesWithOffset(base16(), "foobar", 4, 0, "");
355   }
356 
testEncodingWithCasing( BaseEncoding encoding, String decoded, String encoded)357   private static void testEncodingWithCasing(
358       BaseEncoding encoding, String decoded, String encoded) {
359     testEncodingWithSeparators(encoding, decoded, encoded);
360     testEncodingWithSeparators(encoding.upperCase(), decoded, Ascii.toUpperCase(encoded));
361     testEncodingWithSeparators(encoding.lowerCase(), decoded, Ascii.toLowerCase(encoded));
362   }
363 
testEncodingWithSeparators( BaseEncoding encoding, String decoded, String encoded)364   private static void testEncodingWithSeparators(
365       BaseEncoding encoding, String decoded, String encoded) {
366     testEncoding(encoding, decoded, encoded);
367 
368     // test separators work
369     for (int sepLength = 3; sepLength <= 5; sepLength++) {
370       for (String separator : ImmutableList.of(",", "\n", ";;", "")) {
371         testEncoding(
372             encoding.withSeparator(separator, sepLength),
373             decoded,
374             Joiner.on(separator).join(Splitter.fixedLength(sepLength).split(encoded)));
375       }
376     }
377   }
378 
testEncoding(BaseEncoding encoding, String decoded, String encoded)379   private static void testEncoding(BaseEncoding encoding, String decoded, String encoded) {
380     testEncodes(encoding, decoded, encoded);
381     testDecodes(encoding, encoded, decoded);
382   }
383 
testEncodes(BaseEncoding encoding, String decoded, String encoded)384   private static void testEncodes(BaseEncoding encoding, String decoded, String encoded) {
385     assertThat(encoding.encode(decoded.getBytes(UTF_8))).isEqualTo(encoded);
386   }
387 
testEncodesWithOffset( BaseEncoding encoding, String decoded, int offset, int len, String encoded)388   private static void testEncodesWithOffset(
389       BaseEncoding encoding, String decoded, int offset, int len, String encoded) {
390     assertThat(encoding.encode(decoded.getBytes(UTF_8), offset, len)).isEqualTo(encoded);
391   }
392 
testDecodes(BaseEncoding encoding, String encoded, String decoded)393   private static void testDecodes(BaseEncoding encoding, String encoded, String decoded) {
394     assertTrue(encoding.canDecode(encoded));
395     assertThat(encoding.decode(encoded)).isEqualTo(decoded.getBytes(UTF_8));
396   }
397 
testDecodesByBytes(BaseEncoding encoding, String encoded, byte[] decoded)398   private static void testDecodesByBytes(BaseEncoding encoding, String encoded, byte[] decoded) {
399     assertTrue(encoding.canDecode(encoded));
400     assertThat(encoding.decode(encoded)).isEqualTo(decoded);
401   }
402 
assertFailsToDecode(BaseEncoding encoding, String cannotDecode)403   private static void assertFailsToDecode(BaseEncoding encoding, String cannotDecode) {
404     assertFailsToDecode(encoding, cannotDecode, null);
405   }
406 
assertFailsToDecode( BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage)407   private static void assertFailsToDecode(
408       BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) {
409     // We use this somewhat weird pattern with an enum for each assertion we want to make as a way
410     // of dealing with the fact that one of the assertions is @GwtIncompatible but we don't want to
411     // have to have duplicate @GwtIncompatible test methods just to make that assertion.
412     for (AssertFailsToDecodeStrategy strategy : AssertFailsToDecodeStrategy.values()) {
413       strategy.assertFailsToDecode(encoding, cannotDecode, expectedMessage);
414     }
415   }
416 
417   enum AssertFailsToDecodeStrategy {
418     @GwtIncompatible // decodingStream(Reader)
419     DECODING_STREAM {
420       @Override
assertFailsToDecode( BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage)421       void assertFailsToDecode(
422           BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) {
423         // Regression test for case where DecodingException was swallowed by default implementation
424         // of
425         // InputStream.read(byte[], int, int)
426         // See https://github.com/google/guava/issues/3542
427         Reader reader = new StringReader(cannotDecode);
428         InputStream decodingStream = encoding.decodingStream(reader);
429         try {
430           ByteStreams.exhaust(decodingStream);
431           fail("Expected DecodingException");
432         } catch (DecodingException expected) {
433           // Don't assert on the expectedMessage; the messages for exceptions thrown from the
434           // decoding stream may differ from the messages for the decode methods.
435         } catch (IOException e) {
436           fail("Expected DecodingException but got: " + e);
437         }
438       }
439     },
440     CAN_DECODE {
441       @Override
assertFailsToDecode( BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage)442       void assertFailsToDecode(
443           BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) {
444         assertFalse(encoding.canDecode(cannotDecode));
445       }
446     },
447     DECODE {
448       @Override
assertFailsToDecode( BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage)449       void assertFailsToDecode(
450           BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) {
451         try {
452           encoding.decode(cannotDecode);
453           fail("Expected IllegalArgumentException");
454         } catch (IllegalArgumentException expected) {
455           if (expectedMessage != null) {
456             assertThat(expected).hasCauseThat().hasMessageThat().isEqualTo(expectedMessage);
457           }
458         }
459       }
460     },
461     DECODE_CHECKED {
462       @Override
assertFailsToDecode( BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage)463       void assertFailsToDecode(
464           BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) {
465         try {
466           encoding.decodeChecked(cannotDecode);
467           fail("Expected DecodingException");
468         } catch (DecodingException expected) {
469           if (expectedMessage != null) {
470             assertThat(expected).hasMessageThat().isEqualTo(expectedMessage);
471           }
472         }
473       }
474     };
475 
assertFailsToDecode( BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage)476     abstract void assertFailsToDecode(
477         BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage);
478   }
479 
480   @GwtIncompatible // Reader/Writer
testStreamingEncodingWithCasing( BaseEncoding encoding, String decoded, String encoded)481   private static void testStreamingEncodingWithCasing(
482       BaseEncoding encoding, String decoded, String encoded) throws IOException {
483     testStreamingEncodingWithSeparators(encoding, decoded, encoded);
484     testStreamingEncodingWithSeparators(encoding.upperCase(), decoded, Ascii.toUpperCase(encoded));
485     testStreamingEncodingWithSeparators(encoding.lowerCase(), decoded, Ascii.toLowerCase(encoded));
486   }
487 
488   @GwtIncompatible // Reader/Writer
testStreamingEncodingWithSeparators( BaseEncoding encoding, String decoded, String encoded)489   private static void testStreamingEncodingWithSeparators(
490       BaseEncoding encoding, String decoded, String encoded) throws IOException {
491     testStreamingEncoding(encoding, decoded, encoded);
492 
493     // test separators work
494     for (int sepLength = 3; sepLength <= 5; sepLength++) {
495       for (String separator : ImmutableList.of(",", "\n", ";;", "")) {
496         testStreamingEncoding(
497             encoding.withSeparator(separator, sepLength),
498             decoded,
499             Joiner.on(separator).join(Splitter.fixedLength(sepLength).split(encoded)));
500       }
501     }
502   }
503 
504   @GwtIncompatible // Reader/Writer
testStreamingEncoding(BaseEncoding encoding, String decoded, String encoded)505   private static void testStreamingEncoding(BaseEncoding encoding, String decoded, String encoded)
506       throws IOException {
507     testStreamingEncodes(encoding, decoded, encoded);
508     testStreamingDecodes(encoding, encoded, decoded);
509   }
510 
511   @GwtIncompatible // Writer
testStreamingEncodes(BaseEncoding encoding, String decoded, String encoded)512   private static void testStreamingEncodes(BaseEncoding encoding, String decoded, String encoded)
513       throws IOException {
514     StringWriter writer = new StringWriter();
515     OutputStream encodingStream = encoding.encodingStream(writer);
516     encodingStream.write(decoded.getBytes(UTF_8));
517     encodingStream.close();
518     assertThat(writer.toString()).isEqualTo(encoded);
519   }
520 
521   @GwtIncompatible // Reader
testStreamingDecodes(BaseEncoding encoding, String encoded, String decoded)522   private static void testStreamingDecodes(BaseEncoding encoding, String encoded, String decoded)
523       throws IOException {
524     byte[] bytes = decoded.getBytes(UTF_8);
525     InputStream decodingStream = encoding.decodingStream(new StringReader(encoded));
526     for (int i = 0; i < bytes.length; i++) {
527       assertThat(decodingStream.read()).isEqualTo(bytes[i] & 0xFF);
528     }
529     assertThat(decodingStream.read()).isEqualTo(-1);
530     decodingStream.close();
531   }
532 
testToString()533   public void testToString() {
534     assertEquals("BaseEncoding.base64().withPadChar('=')", base64().toString());
535     assertEquals("BaseEncoding.base32Hex().omitPadding()", base32Hex().omitPadding().toString());
536     assertEquals(
537         "BaseEncoding.base32().lowerCase().withPadChar('$')",
538         base32().lowerCase().withPadChar('$').toString());
539     assertEquals(
540         "BaseEncoding.base16().withSeparator(\"\n\", 10)",
541         base16().withSeparator("\n", 10).toString());
542   }
543 }
544