/**
* Copyright 2002 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.waveprotocol.wave.model.util;
import junit.framework.TestCase;
import java.util.Arrays;
public class CharBase64Test extends TestCase {
private final byte [] emptyBytes = {};
private final char [] emptyChars = {};
private final String latin1 = "L\u00e1t\u00ef\u00f1-1\n";
private final byte[] latin1Bytes = new byte[] { 76, -31, 116, -17, -15, 45, 49, 10 };
private final String latin1Enc = "TOF07/EtMQo=";
private final String latin1WSEnc = "TOF07_EtMQo="; // web safe
private final String latin1WSNPEnc = "TOF07_EtMQo"; // web safe, no padding
private final byte[] randomBytes = {
(byte)0x58, (byte)0x37, (byte)0xf8, (byte)0x77,
(byte)0xd9, (byte)0x99, (byte)0x17, (byte)0x96
};
private final String randomEnc = "WDf4d9mZF5Y=";
private final String randomWSNPEnc = "WDf4d9mZF5Y";
public void testEncodings() throws Exception {
assertArraysEqual(emptyChars, CharBase64.encode(emptyBytes).toCharArray());
String enc = CharBase64.encode("fastening my ankle to a stone".getBytes());
assertArraysEqual("ZmFzdGVuaW5nIG15IGFua2xlIHRvIGEgc3RvbmU=".toCharArray(),
enc.toCharArray());
assertEquals(latin1Enc, CharBase64.encode(latin1Bytes));
assertEquals(latin1Enc,
CharBase64.encode(offset5(latin1Bytes), 5, latin1Bytes.length,
CharBase64.getAlphabet(), true));
assertEquals(latin1WSEnc,
CharBase64.encodeWebSafe(latin1Bytes, true));
assertEquals(latin1WSNPEnc,
CharBase64.encodeWebSafe(latin1Bytes, false));
assertEquals(randomEnc, CharBase64.encode(randomBytes));
assertEquals(randomWSNPEnc, CharBase64.encodeWebSafe(randomBytes, false));
}
public void testDecodings() throws Exception {
assertArraysEqual(emptyBytes, CharBase64.decode(emptyChars));
char [] d = "d2FzaCB5b3VyIGV5ZWxpZHMgaW4gdGhlIHJhaW4K".toCharArray();
assertArraysEqual("wash your eyelids in the rain\n".getBytes(),
CharBase64.decode(d));
assertArraysEqual(latin1Bytes, CharBase64.decode(latin1Enc));
assertArraysEqual(
latin1Bytes,
CharBase64.decode(offset5(latin1Enc.toCharArray()), 5, latin1Enc.length()));
assertArraysEqual(randomBytes, CharBase64.decode(randomEnc));
}
/** Test decodings that are incomplete. These are officially out of
** spec and 'mimencode -u' complains about EOF, but Google, particularly
** the web safe decoding, instead just assumes there should have been =
** padding at the end.
**/
public void testShortDecodings() throws Exception {
byte [] A = "A\n".getBytes();
assertArraysEqual(A, CharBase64.decode("QQo=".toCharArray()));
assertArraysEqual(A, CharBase64.decode("QQo".toCharArray()));
assertArraysEqual(A, CharBase64.decodeWebSafe("QQo=".toCharArray()));
assertArraysEqual(A, CharBase64.decodeWebSafe("QQo".toCharArray()));
byte [] seven0 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
assertArraysEqual(seven0, CharBase64.decode("AAAAAAAAAA==".toCharArray()));
assertArraysEqual(seven0, CharBase64.decode("AAAAAAAAAA=".toCharArray()));
assertArraysEqual(seven0, CharBase64.decode("AAAAAAAAAA".toCharArray()));
assertArraysEqual(seven0, CharBase64.decodeWebSafe("AAAAAAAAAA==".toCharArray()));
assertArraysEqual(seven0, CharBase64.decodeWebSafe("AAAAAAAAAA=".toCharArray()));
assertArraysEqual(seven0, CharBase64.decodeWebSafe("AAAAAAAAAA".toCharArray()));
}
public void testRoundTrip() throws Exception {
String [] testStrings = {
"", "abcd", "from time to time I am sublime", latin1
};
byte[][] testBytes = {
new byte[0],
new byte[] {97, 98, 99, 100},
new byte[] {102, 114, 111, 109, 32, 116, 105, 109, 101, 32, 116, 111, 32, 116, 105, 109,
101, 32, 73, 32, 97, 109, 32, 115, 117, 98, 108, 105, 109, 101},
latin1Bytes
};
for (int i = 0; i < testStrings.length; i++) {
byte [] bytes = testBytes[i];
String enc = CharBase64.encode(bytes);
byte [] newBytes = CharBase64.decode(enc);
String newEnc = CharBase64.encode(newBytes);
assertArraysEqual(bytes, newBytes);
assertEquals(enc, newEnc);
}
}
public void testLineLength() throws Exception {
String [] testStrings = {
"", "abcdefgh", "abcdefghi", "abcdefghij", "abcdefghijk",
"abcdefghijkl", "abcdefghijklm", "abcdefghijklmn",
"01234567890123456789012345678901234567890123456789" +
"01234567890123456789012345678901234567890123456789"
};
byte[][] testBytes = {
new byte[0],
new byte[] {98, 99, 100, 101, 102, 103, 104},
new byte[] {97, 98, 99, 100, 101, 102, 103, 104, 105},
new byte[] {97, 98, 99, 100, 101, 102, 103, 104, 105, 106},
new byte[] {97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107},
new byte[] {97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108},
new byte[] {97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109},
new byte[] {97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110},
new byte[] {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57}
};
for (int lineLength = 8; lineLength < 80; lineLength += 4) {
for (int i = 0; i < testStrings.length; i++) {
byte [] bytes = testBytes[i];
char[] enc = CharBase64.encode(bytes, 0, bytes.length,
CharBase64.getAlphabet(), lineLength);
byte [] newBytes = CharBase64.decode(new String(enc));
assertArraysEqual(bytes, newBytes);
// Check for newlines
for (int p = lineLength; p < enc.length; p += lineLength + 1) {
assertEquals('\n', enc[p]);
}
}
}
}
/**
* Tests the base64 padding de-mangler. Up to three uncoded bytes are encoded
* as 4 ASCII chars, with padding as necessary so length % 4 == 0; in these
* cases we have the wrong number of padding bytes, but we expect the decoder
* to handle them.
*/
public void testDecodingStringsWithMismatchedPadding() throws Exception {
assertDecodesToString("a", "YQ");
assertDecodesToString("a", "YQ=");
assertDecodesToString("a", "YQ=="); // Correct base64.
assertDecodesToString("a", "YQ===");
assertDecodesToString("a", "YQ====");
assertDecodesToString("ab", "YWI");
assertDecodesToString("ab", "YWI="); // Correct base64.
assertDecodesToString("ab", "YWI==");
assertDecodesToString("ab", "YWI===");
assertDecodesToString("abc", "YWJj"); // Correct base64.
assertDecodesToString("abc", "YWJj=");
assertDecodesToString("abc", "YWJj==");
}
/** Test bad decodings - these are invalid. **/
public void testDecodingInvalidStrings() {
// These contain characters not in the decodabet.
assertFailsToDecode("\u007f");
assertFailsToDecode("Wf2!");
// This sentence just isn't base64 encoded.
assertFailsToDecode("let's not talk of love or chains!");
// a 4n+1 length string is never legal base64
assertFailsToDecode("12345");
}
/**
* Tests badly padded base64 - padding must never occur in the first two bytes
* and must always be at the end of the encoding or followed by more padding.
*/
public void testDecodingStringsWithInvalidPadding() {
assertFailsToDecode("====");
assertFailsToDecode("=Wf2");
assertFailsToDecode("Wf=2");
assertFailsToDecode("Wf==2");
assertFailsToDecode("Wf=2=");
}
private static void assertDecodesToString(String expected, String base64)
throws Exception {
assertEquals(expected, new String(CharBase64.decode(base64)));
}
private static void assertFailsToDecode(String base64) {
try {
CharBase64.decode(base64);
fail("Should have thrown Base64DecoderException for " + base64);
} catch (Base64DecoderException expected) {
}
}
/** Returns a new array with five bytes of '@' followed by {@code bytes}. */
private static byte[] offset5(byte[] bytes) {
byte[] ret = new byte[5 + bytes.length];
for (int i = 0; i < 5; ++i) {
ret[i] = '@';
}
System.arraycopy(bytes, 0, ret, 5, bytes.length);
return ret;
}
/** Returns a new array with five bytes of '@' followed by {@code bytes}. */
private static char[] offset5(char[] chars) {
char[] ret = new char[5 + chars.length];
for (int i = 0; i < 5; ++i) {
ret[i] = '@';
}
System.arraycopy(chars, 0, ret, 5, chars.length);
return ret;
}
static void assertArraysEqual(byte[] expected, byte[] actual) {
assertTrue("expected:<" + new String(expected) +
"> but was:<" + new String(actual) + ">",
Arrays.equals(expected, actual));
}
static void assertArraysEqual(char[] expected, char[] actual) {
assertTrue("expected:<" + new String(expected) +
"> but was:<" + new String(actual) + ">",
Arrays.equals(expected, actual));
}
}