| 1 | /* |
|---|
| 2 | * $Id$ |
|---|
| 3 | * |
|---|
| 4 | * Copyright 2006, The jCoderZ.org Project. All rights reserved. |
|---|
| 5 | * |
|---|
| 6 | * Redistribution and use in source and binary forms, with or without |
|---|
| 7 | * modification, are permitted provided that the following conditions are |
|---|
| 8 | * met: |
|---|
| 9 | * |
|---|
| 10 | * * Redistributions of source code must retain the above copyright |
|---|
| 11 | * notice, this list of conditions and the following disclaimer. |
|---|
| 12 | * * Redistributions in binary form must reproduce the above |
|---|
| 13 | * copyright notice, this list of conditions and the following |
|---|
| 14 | * disclaimer in the documentation and/or other materials |
|---|
| 15 | * provided with the distribution. |
|---|
| 16 | * * Neither the name of the jCoderZ.org Project nor the names of |
|---|
| 17 | * its contributors may be used to endorse or promote products |
|---|
| 18 | * derived from this software without specific prior written |
|---|
| 19 | * permission. |
|---|
| 20 | * |
|---|
| 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND |
|---|
| 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|---|
| 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|---|
| 24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS |
|---|
| 25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|---|
| 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|---|
| 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
|---|
| 28 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
|---|
| 29 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
|---|
| 30 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
|---|
| 31 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 32 | */ |
|---|
| 33 | package org.jcoderz.commons.util; |
|---|
| 34 | |
|---|
| 35 | import java.util.Arrays; |
|---|
| 36 | import java.util.Random; |
|---|
| 37 | import junit.framework.TestCase; |
|---|
| 38 | import org.jcoderz.commons.ArgumentMalformedException; |
|---|
| 39 | |
|---|
| 40 | |
|---|
| 41 | /** |
|---|
| 42 | * JUnit Test for the class {@link org.jcoderz.commons.util.Base64Util}. |
|---|
| 43 | * |
|---|
| 44 | * @author Andreas Mandel |
|---|
| 45 | * @author Michael Griffel |
|---|
| 46 | */ |
|---|
| 47 | public class Base64UtilTest |
|---|
| 48 | extends TestCase |
|---|
| 49 | { |
|---|
| 50 | private static final int TESTDATA_LENGTH = 4097; |
|---|
| 51 | private static final byte[] TESTDATA; |
|---|
| 52 | |
|---|
| 53 | private static final String TEST_BASE_64_ENCODED_1 = "dGVzdA=="; |
|---|
| 54 | private static final String TEST_BASE_64_DECODED_1 = "test"; |
|---|
| 55 | private static final String TEST_BASE_64_ENCODED_2 |
|---|
| 56 | = "VGhpcyBpcyBhIGxvbmcgdGVzdCBtZXNzYWdlLiBVc2VkIHRvIHRlc3QgdGhlIHRl" |
|---|
| 57 | + "c3Qu"; |
|---|
| 58 | private static final String TEST_BASE_64_DECODED_2 |
|---|
| 59 | = "This is a long test message. Used to test the test."; |
|---|
| 60 | private static final int PERFORMANCE_LOOP_COUNT = 10000; |
|---|
| 61 | static |
|---|
| 62 | { |
|---|
| 63 | TESTDATA = new byte[TESTDATA_LENGTH]; |
|---|
| 64 | new Random().nextBytes(TESTDATA); |
|---|
| 65 | } |
|---|
| 66 | |
|---|
| 67 | /** Test short string. */ |
|---|
| 68 | public void testString1 () |
|---|
| 69 | { |
|---|
| 70 | assertEquals("Short test string.", TEST_BASE_64_DECODED_1, |
|---|
| 71 | StringUtil.asciiToString( |
|---|
| 72 | Base64Util.decode(TEST_BASE_64_ENCODED_1))); |
|---|
| 73 | } |
|---|
| 74 | |
|---|
| 75 | /** Test long string. */ |
|---|
| 76 | public void testString2 () |
|---|
| 77 | { |
|---|
| 78 | assertEquals("Long test string.", TEST_BASE_64_DECODED_2, |
|---|
| 79 | StringUtil.asciiToString( |
|---|
| 80 | Base64Util.decode(TEST_BASE_64_ENCODED_2))); |
|---|
| 81 | } |
|---|
| 82 | |
|---|
| 83 | /** Test encode decode sequence. */ |
|---|
| 84 | public void testEncodeDecodeBinary () |
|---|
| 85 | { |
|---|
| 86 | checkEncodeDecode(TESTDATA); |
|---|
| 87 | byte[] testdata; |
|---|
| 88 | testdata = new byte[TESTDATA.length - 1]; |
|---|
| 89 | System.arraycopy(TESTDATA, 0, testdata, 0, testdata.length); |
|---|
| 90 | checkEncodeDecode(testdata); |
|---|
| 91 | testdata = new byte[testdata.length - 1]; |
|---|
| 92 | System.arraycopy(TESTDATA, 0, testdata, 0, testdata.length); |
|---|
| 93 | checkEncodeDecode(testdata); |
|---|
| 94 | testdata = new byte[testdata.length - 1]; |
|---|
| 95 | System.arraycopy(TESTDATA, 0, testdata, 0, testdata.length); |
|---|
| 96 | checkEncodeDecode(testdata); |
|---|
| 97 | testdata = new byte[testdata.length - 1]; |
|---|
| 98 | System.arraycopy(TESTDATA, 0, testdata, 0, testdata.length); |
|---|
| 99 | checkEncodeDecode(testdata); |
|---|
| 100 | testdata = new byte[testdata.length - 1]; |
|---|
| 101 | System.arraycopy(TESTDATA, 0, testdata, 0, testdata.length); |
|---|
| 102 | checkEncodeDecode(testdata); |
|---|
| 103 | } |
|---|
| 104 | |
|---|
| 105 | /** |
|---|
| 106 | * Encoding performance test. |
|---|
| 107 | */ |
|---|
| 108 | public void xxxtestEncodePerformanceRef () |
|---|
| 109 | { |
|---|
| 110 | final long start = System.currentTimeMillis(); |
|---|
| 111 | for (int i = 0; i < PERFORMANCE_LOOP_COUNT; ++i) |
|---|
| 112 | { |
|---|
| 113 | // TOOD: ref impl: Base64.encodeToString(TESTDATA, false); |
|---|
| 114 | } |
|---|
| 115 | final long diff = System.currentTimeMillis() - start; |
|---|
| 116 | |
|---|
| 117 | System.out.println("Base64 encoding (ref) " |
|---|
| 118 | + (PERFORMANCE_LOOP_COUNT * TESTDATA.length) / diff + "kB/sec"); |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | /** |
|---|
| 122 | * Encoding performance test. |
|---|
| 123 | */ |
|---|
| 124 | public void xxxtestEncodePerformance () |
|---|
| 125 | { |
|---|
| 126 | final long start = System.currentTimeMillis(); |
|---|
| 127 | for (int i = 0; i < PERFORMANCE_LOOP_COUNT; ++i) |
|---|
| 128 | { |
|---|
| 129 | Base64Util.encode(TESTDATA); |
|---|
| 130 | } |
|---|
| 131 | final long diff = System.currentTimeMillis() - start; |
|---|
| 132 | |
|---|
| 133 | System.out.println("Base64 encoding (enc) " |
|---|
| 134 | + (PERFORMANCE_LOOP_COUNT * TESTDATA.length) / diff + "kB/sec"); |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | /** |
|---|
| 138 | * Encoding performance test. |
|---|
| 139 | */ |
|---|
| 140 | public void xxxtestEncodePerformanceRef2 () |
|---|
| 141 | { |
|---|
| 142 | final long start = System.currentTimeMillis(); |
|---|
| 143 | for (int i = 0; i < PERFORMANCE_LOOP_COUNT; ++i) |
|---|
| 144 | { |
|---|
| 145 | // TOOD: ref impl: Base64.encodeToString(TESTDATA, false); |
|---|
| 146 | } |
|---|
| 147 | final long diff = System.currentTimeMillis() - start; |
|---|
| 148 | |
|---|
| 149 | System.out.println("Base64 encoding (ref2) " |
|---|
| 150 | + (PERFORMANCE_LOOP_COUNT * TESTDATA.length) / diff + "kB/sec"); |
|---|
| 151 | } |
|---|
| 152 | |
|---|
| 153 | /** |
|---|
| 154 | * Encoding performance test. |
|---|
| 155 | */ |
|---|
| 156 | public void xxxtestEncodePerformance2 () |
|---|
| 157 | { |
|---|
| 158 | final long start = System.currentTimeMillis(); |
|---|
| 159 | for (int i = 0; i < PERFORMANCE_LOOP_COUNT; ++i) |
|---|
| 160 | { |
|---|
| 161 | Base64Util.encode(TESTDATA); |
|---|
| 162 | } |
|---|
| 163 | final long diff = System.currentTimeMillis() - start; |
|---|
| 164 | |
|---|
| 165 | System.out.println("Base64 encoding (enc2) " |
|---|
| 166 | + (PERFORMANCE_LOOP_COUNT * TESTDATA.length) / diff + "kB/sec"); |
|---|
| 167 | } |
|---|
| 168 | /** |
|---|
| 169 | * Decoding performance test. |
|---|
| 170 | */ |
|---|
| 171 | public void xxxtestDecodePerformance () |
|---|
| 172 | { |
|---|
| 173 | final long start = System.currentTimeMillis(); |
|---|
| 174 | |
|---|
| 175 | final String TEST_ENCODE = Base64Util.encode(TESTDATA); |
|---|
| 176 | for (int i = 0; i < PERFORMANCE_LOOP_COUNT; ++i) |
|---|
| 177 | { |
|---|
| 178 | Base64Util.decode(TEST_ENCODE); |
|---|
| 179 | } |
|---|
| 180 | final long diff = System.currentTimeMillis() - start; |
|---|
| 181 | |
|---|
| 182 | System.out.println("Base64 decoding " |
|---|
| 183 | + (PERFORMANCE_LOOP_COUNT * TEST_ENCODE.length()) / diff |
|---|
| 184 | + "kB/sec"); |
|---|
| 185 | } |
|---|
| 186 | |
|---|
| 187 | /** |
|---|
| 188 | * Test vectors for encoding. Taken from the Apache commons project. |
|---|
| 189 | */ |
|---|
| 190 | public void testKnownEncodings () |
|---|
| 191 | { |
|---|
| 192 | assertEquals("test encoding with text", |
|---|
| 193 | "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBk" |
|---|
| 194 | + "b2dzLg==", |
|---|
| 195 | Base64Util.encode("The quick brown fox jumped over the lazy dogs." |
|---|
| 196 | .getBytes())); |
|---|
| 197 | assertEquals("test encoding with text", |
|---|
| 198 | "SXQgd2FzIHRoZSBiZXN0IG9mIHRpbWVzLCBpdCB3YXMgdGhlIHdvcnN0" |
|---|
| 199 | + "IG9mIHRpbWVzLg==", |
|---|
| 200 | Base64Util.encode( |
|---|
| 201 | "It was the best of times, it was the worst of times." |
|---|
| 202 | .getBytes())); |
|---|
| 203 | assertEquals("test encoding with URL", |
|---|
| 204 | "aHR0cDovL2pha2FydGEuYXBhY2hlLm9yZy9jb21tbW9ucw==", |
|---|
| 205 | Base64Util.encode("http://jakarta.apache.org/commmons".getBytes())); |
|---|
| 206 | |
|---|
| 207 | assertEquals("test encoding with all letters", |
|---|
| 208 | "QWFCYkNjRGRFZUZmR2dIaElpSmpLa0xsTW1Obk9vUHBRcVJyU3NUdFV1" |
|---|
| 209 | + "VnZXd1h4WXlaeg==", |
|---|
| 210 | Base64Util.encode( |
|---|
| 211 | "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" |
|---|
| 212 | .getBytes())); |
|---|
| 213 | assertEquals("test encoding with digits", |
|---|
| 214 | "eyAwLCAxLCAyLCAzLCA0LCA1LCA2LCA3LCA4LCA5IH0=", |
|---|
| 215 | Base64Util.encode("{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }".getBytes())); |
|---|
| 216 | assertEquals("eHl6enkh", |
|---|
| 217 | Base64Util.encode("xyzzy!".getBytes())); |
|---|
| 218 | } |
|---|
| 219 | |
|---|
| 220 | /** |
|---|
| 221 | * Test vectors for decoding. Taken from the Apache commons project. |
|---|
| 222 | */ |
|---|
| 223 | public void testKnownDecodings () |
|---|
| 224 | { |
|---|
| 225 | assertEquals("test decoding with text", |
|---|
| 226 | "The quick brown fox jumped over the lazy dogs.", |
|---|
| 227 | StringUtil.asciiToString(Base64Util.decode( |
|---|
| 228 | "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2dzLg" |
|---|
| 229 | + "=="))); |
|---|
| 230 | assertEquals("test decoding with text", |
|---|
| 231 | "It was the best of times, it was the worst of times.", |
|---|
| 232 | StringUtil.asciiToString(Base64Util.decode( |
|---|
| 233 | "SXQgd2FzIHRoZSBiZXN0IG9mIHRpbWVzLCBpdCB3YXMgdGhlIHdvcnN0IG9mIH" |
|---|
| 234 | + "RpbWVzLg=="))); |
|---|
| 235 | assertEquals("test decoding with URL", |
|---|
| 236 | "http://jakarta.apache.org/commmons", |
|---|
| 237 | StringUtil.asciiToString(Base64Util.decode( |
|---|
| 238 | "aHR0cDovL2pha2FydGEuYXBhY2hlLm9yZy9jb21tbW9ucw=="))); |
|---|
| 239 | |
|---|
| 240 | assertEquals("test decoding with letters", |
|---|
| 241 | "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz", |
|---|
| 242 | StringUtil.asciiToString(Base64Util.decode( |
|---|
| 243 | "QWFCYkNjRGRFZUZmR2dIaElpSmpLa0xsTW1Obk9vUHBRcVJyU3NUdFV1VnZXd1" |
|---|
| 244 | + "h4WXlaeg=="))); |
|---|
| 245 | assertEquals("test decoding with digits", |
|---|
| 246 | "{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }", |
|---|
| 247 | StringUtil.asciiToString(Base64Util.decode( |
|---|
| 248 | "eyAwLCAxLCAyLCAzLCA0LCA1LCA2LCA3LCA4LCA5IH0="))); |
|---|
| 249 | assertEquals("test decoding with another test vector", |
|---|
| 250 | "xyzzy!", |
|---|
| 251 | StringUtil.asciiToString(Base64Util.decode("eHl6enkh"))); |
|---|
| 252 | } |
|---|
| 253 | |
|---|
| 254 | /** |
|---|
| 255 | * Test vectors for encoding. Taken from the Apache commons project. |
|---|
| 256 | */ |
|---|
| 257 | public void testSingletons () |
|---|
| 258 | { |
|---|
| 259 | final String[] testVectors = new String[]{ |
|---|
| 260 | "AA==", "AQ==", "Ag==", |
|---|
| 261 | "Aw==", "BA==", "BQ==", "Bg==", "Bw==", "CA==", "CQ==", "Cg==", |
|---|
| 262 | "Cw==", "DA==", "DQ==", "Dg==", "Dw==", "EA==", "EQ==", "Eg==", |
|---|
| 263 | "Ew==", "FA==", "FQ==", "Fg==", "Fw==", "GA==", "GQ==", "Gg==", |
|---|
| 264 | "Gw==", "HA==", "HQ==", "Hg==", "Hw==", "IA==", "IQ==", "Ig==", |
|---|
| 265 | "Iw==", "JA==", "JQ==", "Jg==", "Jw==", "KA==", "KQ==", "Kg==", |
|---|
| 266 | "Kw==", "LA==", "LQ==", "Lg==", "Lw==", "MA==", "MQ==", "Mg==", |
|---|
| 267 | "Mw==", "NA==", "NQ==", "Ng==", "Nw==", "OA==", "OQ==", "Og==", |
|---|
| 268 | "Ow==", "PA==", "PQ==", "Pg==", "Pw==", "QA==", "QQ==", "Qg==", |
|---|
| 269 | "Qw==", "RA==", "RQ==", "Rg==", "Rw==", "SA==", "SQ==", "Sg==", |
|---|
| 270 | "Sw==", "TA==", "TQ==", "Tg==", "Tw==", "UA==", "UQ==", "Ug==", |
|---|
| 271 | "Uw==", "VA==", "VQ==", "Vg==", "Vw==", "WA==", "WQ==", "Wg==", |
|---|
| 272 | "Ww==", "XA==", "XQ==", "Xg==", "Xw==", "YA==", "YQ==", "Yg==", |
|---|
| 273 | "Yw==", "ZA==", "ZQ==", "Zg==", "Zw==", "aA==" |
|---|
| 274 | }; |
|---|
| 275 | for (int i = 0; i < testVectors.length; i++) |
|---|
| 276 | { |
|---|
| 277 | assertEquals("single byte decoding test vector[" + i + "]", |
|---|
| 278 | testVectors[i], Base64Util.encode(new byte[] {(byte) i})); |
|---|
| 279 | } |
|---|
| 280 | } |
|---|
| 281 | |
|---|
| 282 | /** |
|---|
| 283 | * Test vectors with three bytes that tests the Base64 encoding. |
|---|
| 284 | */ |
|---|
| 285 | public void testTriplets () |
|---|
| 286 | { |
|---|
| 287 | final String[] testVectors = new String[]{ |
|---|
| 288 | "AAAA", "AAAB", "AAAC", "AAAD", |
|---|
| 289 | "AAAE", "AAAF", "AAAG", "AAAH", "AAAI", "AAAJ", "AAAK", "AAAL", |
|---|
| 290 | "AAAM", "AAAN", "AAAO", "AAAP", "AAAQ", "AAAR", "AAAS", "AAAT", |
|---|
| 291 | "AAAU", "AAAV", "AAAW", "AAAX", "AAAY", "AAAZ", "AAAa", "AAAb", |
|---|
| 292 | "AAAc", "AAAd", "AAAe", "AAAf", "AAAg", "AAAh", "AAAi", "AAAj", |
|---|
| 293 | "AAAk", "AAAl", "AAAm", "AAAn", "AAAo", "AAAp", "AAAq", "AAAr", |
|---|
| 294 | "AAAs", "AAAt", "AAAu", "AAAv", "AAAw", "AAAx", "AAAy", "AAAz", |
|---|
| 295 | "AAA0", "AAA1", "AAA2", "AAA3", "AAA4", "AAA5", "AAA6", "AAA7", |
|---|
| 296 | "AAA8", "AAA9", "AAA+", "AAA/" |
|---|
| 297 | }; |
|---|
| 298 | for (int i = 0; i < testVectors.length; i++) |
|---|
| 299 | { |
|---|
| 300 | assertEquals("three byte encoding vector[" + i + "]", |
|---|
| 301 | testVectors[i], |
|---|
| 302 | Base64Util.encode(new byte[] {(byte) 0, (byte) 0, (byte) i})); |
|---|
| 303 | } |
|---|
| 304 | } |
|---|
| 305 | |
|---|
| 306 | public void testDecodeWithMalformedData () |
|---|
| 307 | { |
|---|
| 308 | decodeWithMalformedDataTest(null); |
|---|
| 309 | decodeWithMalformedDataTest(""); |
|---|
| 310 | decodeWithMalformedDataTest("a"); |
|---|
| 311 | decodeWithMalformedDataTest("ab"); |
|---|
| 312 | decodeWithMalformedDataTest("abc"); |
|---|
| 313 | decodeWithMalformedDataTest("===="); |
|---|
| 314 | decodeWithMalformedDataTest("3c=r"); |
|---|
| 315 | decodeWithMalformedDataTest("dGVzd==="); |
|---|
| 316 | decodeWithMalformedDataTest("3c=="); |
|---|
| 317 | decodeWithMalformedDataTest("dGVzd!=="); |
|---|
| 318 | decodeWithMalformedDataTest("3cd$"); |
|---|
| 319 | decodeWithMalformedDataTest("3c$d"); |
|---|
| 320 | decodeWithMalformedDataTest("3c$$"); |
|---|
| 321 | } |
|---|
| 322 | |
|---|
| 323 | private void decodeWithMalformedDataTest (String s) |
|---|
| 324 | { |
|---|
| 325 | try |
|---|
| 326 | { |
|---|
| 327 | final byte[] data = Base64Util.decode(s); |
|---|
| 328 | fail("Expected ArgumentMalformedException for the malformed " |
|---|
| 329 | + "base64 data : '" + s + "' result is " |
|---|
| 330 | + HexUtil.bytesToHex(data)); |
|---|
| 331 | } |
|---|
| 332 | catch (ArgumentMalformedException x) |
|---|
| 333 | { |
|---|
| 334 | // expected |
|---|
| 335 | } |
|---|
| 336 | } |
|---|
| 337 | |
|---|
| 338 | private void checkEncodeDecode (byte[] data) |
|---|
| 339 | { |
|---|
| 340 | assertTrue("Encode decode should be idempotent (" + data.length + ")", |
|---|
| 341 | Arrays.equals(data, Base64Util.decode(Base64Util.encode(data)))); |
|---|
| 342 | } |
|---|
| 343 | } |
|---|