• 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.io.BaseEncoding.base16;
18 import static com.google.common.io.BaseEncoding.base32;
19 import static com.google.common.io.BaseEncoding.base32Hex;
20 import static com.google.common.io.BaseEncoding.base64;
21 
22 import com.google.common.annotations.GwtCompatible;
23 import com.google.common.annotations.GwtIncompatible;
24 import com.google.common.base.Ascii;
25 import com.google.common.base.Joiner;
26 import com.google.common.base.Splitter;
27 import com.google.common.collect.ImmutableList;
28 import com.google.common.io.BaseEncoding.DecodingException;
29 
30 import junit.framework.TestCase;
31 
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.OutputStream;
35 import java.io.StringReader;
36 import java.io.StringWriter;
37 import java.io.UnsupportedEncodingException;
38 
39 /**
40  * Tests for {@code BaseEncoding}.
41  *
42  * @author Louis Wasserman
43  */
44 @GwtCompatible(emulated = true)
45 public class BaseEncodingTest extends TestCase {
assertEquals(byte[] expected, byte[] actual)46   public static void assertEquals(byte[] expected, byte[] actual) {
47     assertEquals(expected.length, actual.length);
48     for (int i = 0; i < expected.length; i++) {
49       assertEquals(expected[i], actual[i]);
50     }
51   }
52 
testSeparatorsExplicitly()53   public void testSeparatorsExplicitly() {
54     testEncodes(base64().withSeparator("\n", 3), "foobar", "Zm9\nvYm\nFy");
55     testEncodes(base64().withSeparator("$", 4), "foobar", "Zm9v$YmFy");
56     testEncodes(base32().withSeparator("*", 4), "foobar", "MZXW*6YTB*OI==*====");
57   }
58 
59   @SuppressWarnings("ReturnValueIgnored")
testSeparatorSameAsPadChar()60   public void testSeparatorSameAsPadChar() {
61     try {
62       base64().withSeparator("=", 3);
63       fail("Expected IllegalArgumentException");
64     } catch (IllegalArgumentException expected) {}
65 
66     try {
67       base64().withPadChar('#').withSeparator("!#!", 3);
68       fail("Expected IllegalArgumentException");
69     } catch (IllegalArgumentException expected) {}
70   }
71 
72   @SuppressWarnings("ReturnValueIgnored")
testAtMostOneSeparator()73   public void testAtMostOneSeparator() {
74     BaseEncoding separated = base64().withSeparator("\n", 3);
75     try {
76       separated.withSeparator("$", 4);
77       fail("Expected UnsupportedOperationException");
78     } catch (UnsupportedOperationException expected) {}
79   }
80 
testBase64()81   public void testBase64() {
82     // The following test vectors are specified in RFC 4648 itself
83     testEncodingWithSeparators(base64(), "", "");
84     testEncodingWithSeparators(base64(), "f", "Zg==");
85     testEncodingWithSeparators(base64(), "fo", "Zm8=");
86     testEncodingWithSeparators(base64(), "foo", "Zm9v");
87     testEncodingWithSeparators(base64(), "foob", "Zm9vYg==");
88     testEncodingWithSeparators(base64(), "fooba", "Zm9vYmE=");
89     testEncodingWithSeparators(base64(), "foobar", "Zm9vYmFy");
90   }
91 
92   @GwtIncompatible("Reader/Writer")
testBase64Streaming()93   public void testBase64Streaming() throws IOException {
94     // The following test vectors are specified in RFC 4648 itself
95     testStreamingEncodingWithSeparators(base64(), "", "");
96     testStreamingEncodingWithSeparators(base64(), "f", "Zg==");
97     testStreamingEncodingWithSeparators(base64(), "fo", "Zm8=");
98     testStreamingEncodingWithSeparators(base64(), "foo", "Zm9v");
99     testStreamingEncodingWithSeparators(base64(), "foob", "Zm9vYg==");
100     testStreamingEncodingWithSeparators(base64(), "fooba", "Zm9vYmE=");
101     testStreamingEncodingWithSeparators(base64(), "foobar", "Zm9vYmFy");
102   }
103 
testBase64LenientPadding()104   public void testBase64LenientPadding() {
105     testDecodes(base64(), "Zg", "f");
106     testDecodes(base64(), "Zg=", "f");
107     testDecodes(base64(), "Zg==", "f"); // proper padding length
108     testDecodes(base64(), "Zg===", "f");
109     testDecodes(base64(), "Zg====", "f");
110   }
111 
testBase64InvalidDecodings()112   public void testBase64InvalidDecodings() {
113     // These contain bytes not in the decodabet.
114     assertFailsToDecode(base64(), "\u007f");
115     assertFailsToDecode(base64(), "Wf2!");
116     // This sentence just isn't base64() encoded.
117     assertFailsToDecode(base64(), "let's not talk of love or chains!");
118     // A 4n+1 length string is never legal base64().
119     assertFailsToDecode(base64(), "12345");
120   }
121 
122   @SuppressWarnings("ReturnValueIgnored")
testBase64CannotUpperCase()123   public void testBase64CannotUpperCase() {
124     try {
125       base64().upperCase();
126       fail();
127     } catch (IllegalStateException expected) {
128       // success
129     }
130   }
131 
132   @SuppressWarnings("ReturnValueIgnored")
testBase64CannotLowerCase()133   public void testBase64CannotLowerCase() {
134     try {
135       base64().lowerCase();
136       fail();
137     } catch (IllegalStateException expected) {
138       // success
139     }
140   }
141 
testBase64AlternatePadding()142   public void testBase64AlternatePadding() {
143     BaseEncoding enc = base64().withPadChar('~');
144     testEncodingWithSeparators(enc, "", "");
145     testEncodingWithSeparators(enc, "f", "Zg~~");
146     testEncodingWithSeparators(enc, "fo", "Zm8~");
147     testEncodingWithSeparators(enc, "foo", "Zm9v");
148     testEncodingWithSeparators(enc, "foob", "Zm9vYg~~");
149     testEncodingWithSeparators(enc, "fooba", "Zm9vYmE~");
150     testEncodingWithSeparators(enc, "foobar", "Zm9vYmFy");
151   }
152 
153   @GwtIncompatible("Reader/Writer")
testBase64StreamingAlternatePadding()154   public void testBase64StreamingAlternatePadding() throws IOException {
155     BaseEncoding enc = base64().withPadChar('~');
156     testStreamingEncodingWithSeparators(enc, "", "");
157     testStreamingEncodingWithSeparators(enc, "f", "Zg~~");
158     testStreamingEncodingWithSeparators(enc, "fo", "Zm8~");
159     testStreamingEncodingWithSeparators(enc, "foo", "Zm9v");
160     testStreamingEncodingWithSeparators(enc, "foob", "Zm9vYg~~");
161     testStreamingEncodingWithSeparators(enc, "fooba", "Zm9vYmE~");
162     testStreamingEncodingWithSeparators(enc, "foobar", "Zm9vYmFy");
163   }
164 
testBase64OmitPadding()165   public void testBase64OmitPadding() {
166     BaseEncoding enc = base64().omitPadding();
167     testEncodingWithSeparators(enc, "", "");
168     testEncodingWithSeparators(enc, "f", "Zg");
169     testEncodingWithSeparators(enc, "fo", "Zm8");
170     testEncodingWithSeparators(enc, "foo", "Zm9v");
171     testEncodingWithSeparators(enc, "foob", "Zm9vYg");
172     testEncodingWithSeparators(enc, "fooba", "Zm9vYmE");
173     testEncodingWithSeparators(enc, "foobar", "Zm9vYmFy");
174   }
175 
176   @GwtIncompatible("Reader/Writer")
testBase64StreamingOmitPadding()177   public void testBase64StreamingOmitPadding() throws IOException {
178     BaseEncoding enc = base64().omitPadding();
179     testStreamingEncodingWithSeparators(enc, "", "");
180     testStreamingEncodingWithSeparators(enc, "f", "Zg");
181     testStreamingEncodingWithSeparators(enc, "fo", "Zm8");
182     testStreamingEncodingWithSeparators(enc, "foo", "Zm9v");
183     testStreamingEncodingWithSeparators(enc, "foob", "Zm9vYg");
184     testStreamingEncodingWithSeparators(enc, "fooba", "Zm9vYmE");
185     testStreamingEncodingWithSeparators(enc, "foobar", "Zm9vYmFy");
186   }
187 
testBase32()188   public void testBase32() {
189     // The following test vectors are specified in RFC 4648 itself
190     testEncodingWithCasing(base32(), "", "");
191     testEncodingWithCasing(base32(), "f", "MY======");
192     testEncodingWithCasing(base32(), "fo", "MZXQ====");
193     testEncodingWithCasing(base32(), "foo", "MZXW6===");
194     testEncodingWithCasing(base32(), "foob", "MZXW6YQ=");
195     testEncodingWithCasing(base32(), "fooba", "MZXW6YTB");
196     testEncodingWithCasing(base32(), "foobar", "MZXW6YTBOI======");
197   }
198 
199   @GwtIncompatible("Reader/Writer")
testBase32Streaming()200   public void testBase32Streaming() throws IOException {
201     // The following test vectors are specified in RFC 4648 itself
202     testStreamingEncodingWithCasing(base32(), "", "");
203     testStreamingEncodingWithCasing(base32(), "f", "MY======");
204     testStreamingEncodingWithCasing(base32(), "fo", "MZXQ====");
205     testStreamingEncodingWithCasing(base32(), "foo", "MZXW6===");
206     testStreamingEncodingWithCasing(base32(), "foob", "MZXW6YQ=");
207     testStreamingEncodingWithCasing(base32(), "fooba", "MZXW6YTB");
208     testStreamingEncodingWithCasing(base32(), "foobar", "MZXW6YTBOI======");
209   }
210 
testBase32LenientPadding()211   public void testBase32LenientPadding() {
212     testDecodes(base32(), "MZXW6", "foo");
213     testDecodes(base32(), "MZXW6=", "foo");
214     testDecodes(base32(), "MZXW6==", "foo");
215     testDecodes(base32(), "MZXW6===", "foo"); // proper padding length
216     testDecodes(base32(), "MZXW6====", "foo");
217     testDecodes(base32(), "MZXW6=====", "foo");
218   }
219 
testBase32AlternatePadding()220   public void testBase32AlternatePadding() {
221     BaseEncoding enc = base32().withPadChar('~');
222     testEncodingWithCasing(enc, "", "");
223     testEncodingWithCasing(enc, "f", "MY~~~~~~");
224     testEncodingWithCasing(enc, "fo", "MZXQ~~~~");
225     testEncodingWithCasing(enc, "foo", "MZXW6~~~");
226     testEncodingWithCasing(enc, "foob", "MZXW6YQ~");
227     testEncodingWithCasing(enc, "fooba", "MZXW6YTB");
228     testEncodingWithCasing(enc, "foobar", "MZXW6YTBOI~~~~~~");
229   }
230 
testBase32InvalidDecodings()231   public void testBase32InvalidDecodings() {
232     // These contain bytes not in the decodabet.
233     assertFailsToDecode(base32(), "\u007f");
234     assertFailsToDecode(base32(), "Wf2!");
235     // This sentence just isn't base32() encoded.
236     assertFailsToDecode(base32(), "let's not talk of love or chains!");
237     // An 8n+{1,3,6} length string is never legal base32.
238     assertFailsToDecode(base32(), "A");
239     assertFailsToDecode(base32(), "ABC");
240     assertFailsToDecode(base32(), "ABCDEF");
241   }
242 
testBase32UpperCaseIsNoOp()243   public void testBase32UpperCaseIsNoOp() {
244     assertSame(base32(), base32().upperCase());
245   }
246 
testBase32Hex()247   public void testBase32Hex() {
248     // The following test vectors are specified in RFC 4648 itself
249     testEncodingWithCasing(base32Hex(), "", "");
250     testEncodingWithCasing(base32Hex(), "f", "CO======");
251     testEncodingWithCasing(base32Hex(), "fo", "CPNG====");
252     testEncodingWithCasing(base32Hex(), "foo", "CPNMU===");
253     testEncodingWithCasing(base32Hex(), "foob", "CPNMUOG=");
254     testEncodingWithCasing(base32Hex(), "fooba", "CPNMUOJ1");
255     testEncodingWithCasing(base32Hex(), "foobar", "CPNMUOJ1E8======");
256   }
257 
258   @GwtIncompatible("Reader/Writer")
testBase32HexStreaming()259   public void testBase32HexStreaming() throws IOException {
260     // The following test vectors are specified in RFC 4648 itself
261     testStreamingEncodingWithCasing(base32Hex(), "", "");
262     testStreamingEncodingWithCasing(base32Hex(), "f", "CO======");
263     testStreamingEncodingWithCasing(base32Hex(), "fo", "CPNG====");
264     testStreamingEncodingWithCasing(base32Hex(), "foo", "CPNMU===");
265     testStreamingEncodingWithCasing(base32Hex(), "foob", "CPNMUOG=");
266     testStreamingEncodingWithCasing(base32Hex(), "fooba", "CPNMUOJ1");
267     testStreamingEncodingWithCasing(base32Hex(), "foobar", "CPNMUOJ1E8======");
268   }
269 
testBase32HexLenientPadding()270   public void testBase32HexLenientPadding() {
271     testDecodes(base32Hex(), "CPNMU", "foo");
272     testDecodes(base32Hex(), "CPNMU=", "foo");
273     testDecodes(base32Hex(), "CPNMU==", "foo");
274     testDecodes(base32Hex(), "CPNMU===", "foo"); // proper padding length
275     testDecodes(base32Hex(), "CPNMU====", "foo");
276     testDecodes(base32Hex(), "CPNMU=====", "foo");
277   }
278 
testBase32HexInvalidDecodings()279   public void testBase32HexInvalidDecodings() {
280     // These contain bytes not in the decodabet.
281     assertFailsToDecode(base32Hex(), "\u007f");
282     assertFailsToDecode(base32Hex(), "Wf2!");
283     // This sentence just isn't base32 encoded.
284     assertFailsToDecode(base32Hex(), "let's not talk of love or chains!");
285     // An 8n+{1,3,6} length string is never legal base32.
286     assertFailsToDecode(base32Hex(), "A");
287     assertFailsToDecode(base32Hex(), "ABC");
288     assertFailsToDecode(base32Hex(), "ABCDEF");
289   }
290 
testBase32HexUpperCaseIsNoOp()291   public void testBase32HexUpperCaseIsNoOp() {
292     assertSame(base32Hex(), base32Hex().upperCase());
293   }
294 
testBase16()295   public void testBase16() {
296     testEncodingWithCasing(base16(), "", "");
297     testEncodingWithCasing(base16(), "f", "66");
298     testEncodingWithCasing(base16(), "fo", "666F");
299     testEncodingWithCasing(base16(), "foo", "666F6F");
300     testEncodingWithCasing(base16(), "foob", "666F6F62");
301     testEncodingWithCasing(base16(), "fooba", "666F6F6261");
302     testEncodingWithCasing(base16(), "foobar", "666F6F626172");
303   }
304 
testBase16UpperCaseIsNoOp()305   public void testBase16UpperCaseIsNoOp() {
306     assertSame(base16(), base16().upperCase());
307   }
308 
testEncodingWithCasing( BaseEncoding encoding, String decoded, String encoded)309   private static void testEncodingWithCasing(
310       BaseEncoding encoding, String decoded, String encoded) {
311     testEncodingWithSeparators(encoding, decoded, encoded);
312     testEncodingWithSeparators(encoding.upperCase(), decoded, Ascii.toUpperCase(encoded));
313     testEncodingWithSeparators(encoding.lowerCase(), decoded, Ascii.toLowerCase(encoded));
314   }
315 
testEncodingWithSeparators( BaseEncoding encoding, String decoded, String encoded)316   private static void testEncodingWithSeparators(
317       BaseEncoding encoding, String decoded, String encoded) {
318     testEncoding(encoding, decoded, encoded);
319 
320     // test separators work
321     for (int sepLength = 3; sepLength <= 5; sepLength++) {
322       for (String separator : ImmutableList.of(",", "\n", ";;", "")) {
323         testEncoding(encoding.withSeparator(separator, sepLength), decoded,
324             Joiner.on(separator).join(Splitter.fixedLength(sepLength).split(encoded)));
325       }
326     }
327   }
328 
testEncoding(BaseEncoding encoding, String decoded, String encoded)329   private static void testEncoding(BaseEncoding encoding, String decoded, String encoded) {
330     testEncodes(encoding, decoded, encoded);
331     testDecodes(encoding, encoded, decoded);
332   }
333 
testEncodes(BaseEncoding encoding, String decoded, String encoded)334   private static void testEncodes(BaseEncoding encoding, String decoded, String encoded) {
335     byte[] bytes;
336     try {
337       // GWT does not support String.getBytes(Charset)
338       bytes = decoded.getBytes("UTF-8");
339     } catch (UnsupportedEncodingException e) {
340       throw new AssertionError();
341     }
342     assertEquals(encoded, encoding.encode(bytes));
343   }
344 
testDecodes(BaseEncoding encoding, String encoded, String decoded)345   private static void testDecodes(BaseEncoding encoding, String encoded, String decoded) {
346     byte[] bytes;
347     try {
348       // GWT does not support String.getBytes(Charset)
349       bytes = decoded.getBytes("UTF-8");
350     } catch (UnsupportedEncodingException e) {
351       throw new AssertionError();
352     }
353     assertEquals(bytes, encoding.decode(encoded));
354   }
355 
assertFailsToDecode(BaseEncoding encoding, String cannotDecode)356   private static void assertFailsToDecode(BaseEncoding encoding, String cannotDecode) {
357     try {
358       encoding.decode(cannotDecode);
359       fail("Expected IllegalArgumentException");
360     } catch (IllegalArgumentException expected) {
361       // success
362     }
363     try {
364       encoding.decodeChecked(cannotDecode);
365       fail("Expected DecodingException");
366     } catch (DecodingException expected) {
367       // success
368     }
369   }
370 
371   @GwtIncompatible("Reader/Writer")
testStreamingEncodingWithCasing( BaseEncoding encoding, String decoded, String encoded)372   private static void testStreamingEncodingWithCasing(
373       BaseEncoding encoding, String decoded, String encoded) throws IOException {
374     testStreamingEncodingWithSeparators(encoding, decoded, encoded);
375     testStreamingEncodingWithSeparators(encoding.upperCase(), decoded, Ascii.toUpperCase(encoded));
376     testStreamingEncodingWithSeparators(encoding.lowerCase(), decoded, Ascii.toLowerCase(encoded));
377   }
378 
379   @GwtIncompatible("Reader/Writer")
testStreamingEncodingWithSeparators( BaseEncoding encoding, String decoded, String encoded)380   private static void testStreamingEncodingWithSeparators(
381       BaseEncoding encoding, String decoded, String encoded) throws IOException {
382     testStreamingEncoding(encoding, decoded, encoded);
383 
384     // test separators work
385     for (int sepLength = 3; sepLength <= 5; sepLength++) {
386       for (String separator : ImmutableList.of(",", "\n", ";;", "")) {
387         testStreamingEncoding(encoding.withSeparator(separator, sepLength), decoded,
388             Joiner.on(separator).join(Splitter.fixedLength(sepLength).split(encoded)));
389       }
390     }
391   }
392 
393   @GwtIncompatible("Reader/Writer")
testStreamingEncoding(BaseEncoding encoding, String decoded, String encoded)394   private static void testStreamingEncoding(BaseEncoding encoding, String decoded, String encoded)
395       throws IOException {
396     testStreamingEncodes(encoding, decoded, encoded);
397     testStreamingDecodes(encoding, encoded, decoded);
398   }
399 
400   @GwtIncompatible("Writer")
testStreamingEncodes(BaseEncoding encoding, String decoded, String encoded)401   private static void testStreamingEncodes(BaseEncoding encoding, String decoded, String encoded)
402       throws IOException {
403     byte[] bytes;
404     try {
405       // GWT does not support String.getBytes(Charset)
406       bytes = decoded.getBytes("UTF-8");
407     } catch (UnsupportedEncodingException e) {
408       throw new AssertionError();
409     }
410     StringWriter writer = new StringWriter();
411     OutputStream encodingStream = encoding.encodingStream(writer);
412     encodingStream.write(bytes);
413     encodingStream.close();
414     assertEquals(encoded, writer.toString());
415   }
416 
417   @GwtIncompatible("Reader")
testStreamingDecodes(BaseEncoding encoding, String encoded, String decoded)418   private static void testStreamingDecodes(BaseEncoding encoding, String encoded, String decoded)
419       throws IOException {
420     byte[] bytes;
421     try {
422       // GWT does not support String.getBytes(Charset)
423       bytes = decoded.getBytes("UTF-8");
424     } catch (UnsupportedEncodingException e) {
425       throw new AssertionError();
426     }
427     InputStream decodingStream = encoding.decodingStream(new StringReader(encoded));
428     for (int i = 0; i < bytes.length; i++) {
429       assertEquals(bytes[i] & 0xFF, decodingStream.read());
430     }
431     assertEquals(-1, decodingStream.read());
432     decodingStream.close();
433   }
434 
testToString()435   public void testToString() {
436     assertEquals("BaseEncoding.base64().withPadChar(=)", BaseEncoding.base64().toString());
437     assertEquals("BaseEncoding.base32Hex().omitPadding()",
438         BaseEncoding.base32Hex().omitPadding().toString());
439     assertEquals("BaseEncoding.base32().lowerCase().withPadChar($)",
440         BaseEncoding.base32().lowerCase().withPadChar('$').toString());
441     assertEquals("BaseEncoding.base16().withSeparator(\"\n\", 10)",
442         BaseEncoding.base16().withSeparator("\n", 10).toString());
443   }
444 }
445