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 import static com.google.common.io.BaseEncoding.base64Url; 22 import static com.google.common.truth.Truth.assertThat; 23 import static java.nio.charset.StandardCharsets.UTF_8; 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 } 129 } 130 testBase64CannotLowerCase()131 public void testBase64CannotLowerCase() { 132 try { 133 base64().lowerCase(); 134 fail(); 135 } catch (IllegalStateException expected) { 136 } 137 } 138 testBase64CannotIgnoreCase()139 public void testBase64CannotIgnoreCase() { 140 try { 141 base64().ignoreCase(); 142 fail(); 143 } catch (IllegalStateException expected) { 144 } 145 } 146 testBase64AlternatePadding()147 public void testBase64AlternatePadding() { 148 BaseEncoding enc = base64().withPadChar('~'); 149 testEncodingWithSeparators(enc, "", ""); 150 testEncodingWithSeparators(enc, "f", "Zg~~"); 151 testEncodingWithSeparators(enc, "fo", "Zm8~"); 152 testEncodingWithSeparators(enc, "foo", "Zm9v"); 153 testEncodingWithSeparators(enc, "foob", "Zm9vYg~~"); 154 testEncodingWithSeparators(enc, "fooba", "Zm9vYmE~"); 155 testEncodingWithSeparators(enc, "foobar", "Zm9vYmFy"); 156 } 157 158 @GwtIncompatible // Reader/Writer testBase64StreamingAlternatePadding()159 public void testBase64StreamingAlternatePadding() throws IOException { 160 BaseEncoding enc = base64().withPadChar('~'); 161 testStreamingEncodingWithSeparators(enc, "", ""); 162 testStreamingEncodingWithSeparators(enc, "f", "Zg~~"); 163 testStreamingEncodingWithSeparators(enc, "fo", "Zm8~"); 164 testStreamingEncodingWithSeparators(enc, "foo", "Zm9v"); 165 testStreamingEncodingWithSeparators(enc, "foob", "Zm9vYg~~"); 166 testStreamingEncodingWithSeparators(enc, "fooba", "Zm9vYmE~"); 167 testStreamingEncodingWithSeparators(enc, "foobar", "Zm9vYmFy"); 168 } 169 testBase64OmitPadding()170 public void testBase64OmitPadding() { 171 BaseEncoding enc = base64().omitPadding(); 172 testEncodingWithSeparators(enc, "", ""); 173 testEncodingWithSeparators(enc, "f", "Zg"); 174 testEncodingWithSeparators(enc, "fo", "Zm8"); 175 testEncodingWithSeparators(enc, "foo", "Zm9v"); 176 testEncodingWithSeparators(enc, "foob", "Zm9vYg"); 177 testEncodingWithSeparators(enc, "fooba", "Zm9vYmE"); 178 testEncodingWithSeparators(enc, "foobar", "Zm9vYmFy"); 179 } 180 181 @GwtIncompatible // Reader/Writer testBase64StreamingOmitPadding()182 public void testBase64StreamingOmitPadding() throws IOException { 183 BaseEncoding enc = base64().omitPadding(); 184 testStreamingEncodingWithSeparators(enc, "", ""); 185 testStreamingEncodingWithSeparators(enc, "f", "Zg"); 186 testStreamingEncodingWithSeparators(enc, "fo", "Zm8"); 187 testStreamingEncodingWithSeparators(enc, "foo", "Zm9v"); 188 testStreamingEncodingWithSeparators(enc, "foob", "Zm9vYg"); 189 testStreamingEncodingWithSeparators(enc, "fooba", "Zm9vYmE"); 190 testStreamingEncodingWithSeparators(enc, "foobar", "Zm9vYmFy"); 191 } 192 testBase64Offset()193 public void testBase64Offset() { 194 testEncodesWithOffset(base64(), "foobar", 0, 6, "Zm9vYmFy"); 195 testEncodesWithOffset(base64(), "foobar", 1, 5, "b29iYXI="); 196 testEncodesWithOffset(base64(), "foobar", 2, 3, "b2Jh"); 197 testEncodesWithOffset(base64(), "foobar", 3, 1, "Yg=="); 198 testEncodesWithOffset(base64(), "foobar", 4, 0, ""); 199 } 200 testBase64Url()201 public void testBase64Url() { 202 testDecodesByBytes(base64Url(), "_zzz", new byte[] {-1, 60, -13}); 203 testDecodesByBytes(base64Url(), "-zzz", new byte[] {-5, 60, -13}); 204 } 205 testBase64UrlInvalidDecodings()206 public void testBase64UrlInvalidDecodings() { 207 assertFailsToDecode(base64Url(), "+zzz", "Unrecognized character: +"); 208 assertFailsToDecode(base64Url(), "/zzz", "Unrecognized character: /"); 209 } 210 testBase32()211 public void testBase32() { 212 // The following test vectors are specified in RFC 4648 itself 213 testEncodingWithCasing(base32(), "", ""); 214 testEncodingWithCasing(base32(), "f", "MY======"); 215 testEncodingWithCasing(base32(), "fo", "MZXQ===="); 216 testEncodingWithCasing(base32(), "foo", "MZXW6==="); 217 testEncodingWithCasing(base32(), "foob", "MZXW6YQ="); 218 testEncodingWithCasing(base32(), "fooba", "MZXW6YTB"); 219 testEncodingWithCasing(base32(), "foobar", "MZXW6YTBOI======"); 220 } 221 222 @GwtIncompatible // Reader/Writer testBase32Streaming()223 public void testBase32Streaming() throws IOException { 224 // The following test vectors are specified in RFC 4648 itself 225 testStreamingEncodingWithCasing(base32(), "", ""); 226 testStreamingEncodingWithCasing(base32(), "f", "MY======"); 227 testStreamingEncodingWithCasing(base32(), "fo", "MZXQ===="); 228 testStreamingEncodingWithCasing(base32(), "foo", "MZXW6==="); 229 testStreamingEncodingWithCasing(base32(), "foob", "MZXW6YQ="); 230 testStreamingEncodingWithCasing(base32(), "fooba", "MZXW6YTB"); 231 testStreamingEncodingWithCasing(base32(), "foobar", "MZXW6YTBOI======"); 232 } 233 testBase32LenientPadding()234 public void testBase32LenientPadding() { 235 testDecodes(base32(), "MZXW6", "foo"); 236 testDecodes(base32(), "MZXW6=", "foo"); 237 testDecodes(base32(), "MZXW6==", "foo"); 238 testDecodes(base32(), "MZXW6===", "foo"); // proper padding length 239 testDecodes(base32(), "MZXW6====", "foo"); 240 testDecodes(base32(), "MZXW6=====", "foo"); 241 } 242 testBase32AlternatePadding()243 public void testBase32AlternatePadding() { 244 BaseEncoding enc = base32().withPadChar('~'); 245 testEncodingWithCasing(enc, "", ""); 246 testEncodingWithCasing(enc, "f", "MY~~~~~~"); 247 testEncodingWithCasing(enc, "fo", "MZXQ~~~~"); 248 testEncodingWithCasing(enc, "foo", "MZXW6~~~"); 249 testEncodingWithCasing(enc, "foob", "MZXW6YQ~"); 250 testEncodingWithCasing(enc, "fooba", "MZXW6YTB"); 251 testEncodingWithCasing(enc, "foobar", "MZXW6YTBOI~~~~~~"); 252 } 253 testBase32InvalidDecodings()254 public void testBase32InvalidDecodings() { 255 // These contain bytes not in the decodabet. 256 assertFailsToDecode(base32(), "A ", "Unrecognized character: 0x20"); 257 assertFailsToDecode(base32(), "Wf2!", "Unrecognized character: f"); 258 // This sentence just isn't base32() encoded. 259 assertFailsToDecode(base32(), "let's not talk of love or chains!"); 260 // An 8n+{1,3,6} length string is never legal base32. 261 assertFailsToDecode(base32(), "A", "Invalid input length 1"); 262 assertFailsToDecode(base32(), "ABC"); 263 assertFailsToDecode(base32(), "ABCDEF"); 264 // These have a combination of invalid length, unrecognized characters and wrong padding. 265 assertFailsToDecode(base32(), "AB=C", "Unrecognized character: ="); 266 assertFailsToDecode(base32(), "A=BCDE", "Invalid input length 6"); 267 assertFailsToDecode(base32(), "?", "Invalid input length 1"); 268 } 269 testBase32UpperCaseIsNoOp()270 public void testBase32UpperCaseIsNoOp() { 271 assertThat(base32().upperCase()).isSameInstanceAs(base32()); 272 } 273 testBase32LowerCase()274 public void testBase32LowerCase() { 275 testEncodingWithCasing(base32().lowerCase(), "foobar", "mzxw6ytboi======"); 276 } 277 testBase32IgnoreCase()278 public void testBase32IgnoreCase() { 279 BaseEncoding ignoreCase = base32().ignoreCase(); 280 assertThat(ignoreCase).isNotSameInstanceAs(base32()); 281 assertThat(ignoreCase).isSameInstanceAs(base32().ignoreCase()); 282 testDecodes(ignoreCase, "MZXW6YTBOI======", "foobar"); 283 testDecodes(ignoreCase, "mzxw6ytboi======", "foobar"); 284 } 285 testBase32Offset()286 public void testBase32Offset() { 287 testEncodesWithOffset(base32(), "foobar", 0, 6, "MZXW6YTBOI======"); 288 testEncodesWithOffset(base32(), "foobar", 1, 5, "N5XWEYLS"); 289 testEncodesWithOffset(base32(), "foobar", 2, 3, "N5RGC==="); 290 testEncodesWithOffset(base32(), "foobar", 3, 1, "MI======"); 291 testEncodesWithOffset(base32(), "foobar", 4, 0, ""); 292 } 293 testBase32Hex()294 public void testBase32Hex() { 295 // The following test vectors are specified in RFC 4648 itself 296 testEncodingWithCasing(base32Hex(), "", ""); 297 testEncodingWithCasing(base32Hex(), "f", "CO======"); 298 testEncodingWithCasing(base32Hex(), "fo", "CPNG===="); 299 testEncodingWithCasing(base32Hex(), "foo", "CPNMU==="); 300 testEncodingWithCasing(base32Hex(), "foob", "CPNMUOG="); 301 testEncodingWithCasing(base32Hex(), "fooba", "CPNMUOJ1"); 302 testEncodingWithCasing(base32Hex(), "foobar", "CPNMUOJ1E8======"); 303 } 304 305 @GwtIncompatible // Reader/Writer testBase32HexStreaming()306 public void testBase32HexStreaming() throws IOException { 307 // The following test vectors are specified in RFC 4648 itself 308 testStreamingEncodingWithCasing(base32Hex(), "", ""); 309 testStreamingEncodingWithCasing(base32Hex(), "f", "CO======"); 310 testStreamingEncodingWithCasing(base32Hex(), "fo", "CPNG===="); 311 testStreamingEncodingWithCasing(base32Hex(), "foo", "CPNMU==="); 312 testStreamingEncodingWithCasing(base32Hex(), "foob", "CPNMUOG="); 313 testStreamingEncodingWithCasing(base32Hex(), "fooba", "CPNMUOJ1"); 314 testStreamingEncodingWithCasing(base32Hex(), "foobar", "CPNMUOJ1E8======"); 315 } 316 testBase32HexLenientPadding()317 public void testBase32HexLenientPadding() { 318 testDecodes(base32Hex(), "CPNMU", "foo"); 319 testDecodes(base32Hex(), "CPNMU=", "foo"); 320 testDecodes(base32Hex(), "CPNMU==", "foo"); 321 testDecodes(base32Hex(), "CPNMU===", "foo"); // proper padding length 322 testDecodes(base32Hex(), "CPNMU====", "foo"); 323 testDecodes(base32Hex(), "CPNMU=====", "foo"); 324 } 325 testBase32HexInvalidDecodings()326 public void testBase32HexInvalidDecodings() { 327 // These contain bytes not in the decodabet. 328 assertFailsToDecode(base32Hex(), "A\u007f", "Unrecognized character: 0x7f"); 329 assertFailsToDecode(base32Hex(), "Wf2!", "Unrecognized character: W"); 330 // This sentence just isn't base32 encoded. 331 assertFailsToDecode(base32Hex(), "let's not talk of love or chains!"); 332 // An 8n+{1,3,6} length string is never legal base32. 333 assertFailsToDecode(base32Hex(), "A"); 334 assertFailsToDecode(base32Hex(), "ABC"); 335 assertFailsToDecode(base32Hex(), "ABCDEF"); 336 } 337 testBase32HexUpperCaseIsNoOp()338 public void testBase32HexUpperCaseIsNoOp() { 339 assertThat(base32Hex().upperCase()).isSameInstanceAs(base32Hex()); 340 } 341 testBase16()342 public void testBase16() { 343 testEncodingWithCasing(base16(), "", ""); 344 testEncodingWithCasing(base16(), "f", "66"); 345 testEncodingWithCasing(base16(), "fo", "666F"); 346 testEncodingWithCasing(base16(), "foo", "666F6F"); 347 testEncodingWithCasing(base16(), "foob", "666F6F62"); 348 testEncodingWithCasing(base16(), "fooba", "666F6F6261"); 349 testEncodingWithCasing(base16(), "foobar", "666F6F626172"); 350 } 351 testBase16UpperCaseIsNoOp()352 public void testBase16UpperCaseIsNoOp() { 353 assertThat(base16().upperCase()).isSameInstanceAs(base16()); 354 } 355 testBase16LowerCase()356 public void testBase16LowerCase() { 357 BaseEncoding lowerCase = base16().lowerCase(); 358 assertThat(lowerCase).isNotSameInstanceAs(base16()); 359 assertThat(lowerCase).isSameInstanceAs(base16().lowerCase()); 360 testEncodingWithCasing(lowerCase, "foobar", "666f6f626172"); 361 } 362 testBase16IgnoreCase()363 public void testBase16IgnoreCase() { 364 BaseEncoding ignoreCase = base16().ignoreCase(); 365 assertThat(ignoreCase).isNotSameInstanceAs(base16()); 366 assertThat(ignoreCase).isSameInstanceAs(base16().ignoreCase()); 367 testEncodingWithCasing(ignoreCase, "foobar", "666F6F626172"); 368 testDecodes(ignoreCase, "666F6F626172", "foobar"); 369 testDecodes(ignoreCase, "666f6f626172", "foobar"); 370 testDecodes(ignoreCase, "666F6f626172", "foobar"); 371 } 372 testBase16LowerCaseIgnoreCase()373 public void testBase16LowerCaseIgnoreCase() { 374 BaseEncoding ignoreCase = base16().lowerCase().ignoreCase(); 375 assertThat(ignoreCase).isNotSameInstanceAs(base16()); 376 assertThat(ignoreCase).isSameInstanceAs(base16().lowerCase().ignoreCase()); 377 testEncodingWithCasing(ignoreCase, "foobar", "666f6f626172"); 378 testDecodes(ignoreCase, "666F6F626172", "foobar"); 379 testDecodes(ignoreCase, "666f6f626172", "foobar"); 380 testDecodes(ignoreCase, "666F6f626172", "foobar"); 381 } 382 383 // order the methods are called should not matter testBase16IgnoreCaseLowerCase()384 public void testBase16IgnoreCaseLowerCase() { 385 BaseEncoding ignoreCase = base16().ignoreCase().lowerCase(); 386 assertThat(ignoreCase).isNotSameInstanceAs(base16()); 387 testEncodingWithCasing(ignoreCase, "foobar", "666f6f626172"); 388 testDecodes(ignoreCase, "666F6F626172", "foobar"); 389 testDecodes(ignoreCase, "666f6f626172", "foobar"); 390 testDecodes(ignoreCase, "666F6f626172", "foobar"); 391 } 392 testBase16InvalidDecodings()393 public void testBase16InvalidDecodings() { 394 // These contain bytes not in the decodabet. 395 assertFailsToDecode(base16(), "\n\n", "Unrecognized character: 0xa"); 396 assertFailsToDecode(base16(), "EFGH", "Unrecognized character: G"); 397 // Valid base16 strings always have an even length. 398 assertFailsToDecode(base16(), "A", "Invalid input length 1"); 399 assertFailsToDecode(base16(), "ABC"); 400 // These have a combination of invalid length and unrecognized characters. 401 assertFailsToDecode(base16(), "?", "Invalid input length 1"); 402 assertFailsToDecode(base16(), "ab"); 403 assertFailsToDecode(base16().lowerCase(), "AB"); 404 } 405 testBase16Offset()406 public void testBase16Offset() { 407 testEncodesWithOffset(base16(), "foobar", 0, 6, "666F6F626172"); 408 testEncodesWithOffset(base16(), "foobar", 1, 5, "6F6F626172"); 409 testEncodesWithOffset(base16(), "foobar", 2, 3, "6F6261"); 410 testEncodesWithOffset(base16(), "foobar", 3, 1, "62"); 411 testEncodesWithOffset(base16(), "foobar", 4, 0, ""); 412 } 413 testEncodingWithCasing( BaseEncoding encoding, String decoded, String encoded)414 private static void testEncodingWithCasing( 415 BaseEncoding encoding, String decoded, String encoded) { 416 testEncodingWithSeparators(encoding, decoded, encoded); 417 testEncodingWithSeparators(encoding.upperCase(), decoded, Ascii.toUpperCase(encoded)); 418 testEncodingWithSeparators(encoding.lowerCase(), decoded, Ascii.toLowerCase(encoded)); 419 } 420 testEncodingWithSeparators( BaseEncoding encoding, String decoded, String encoded)421 private static void testEncodingWithSeparators( 422 BaseEncoding encoding, String decoded, String encoded) { 423 testEncoding(encoding, decoded, encoded); 424 425 // test separators work 426 for (int sepLength = 3; sepLength <= 5; sepLength++) { 427 for (String separator : ImmutableList.of(",", "\n", ";;", "")) { 428 testEncoding( 429 encoding.withSeparator(separator, sepLength), 430 decoded, 431 Joiner.on(separator).join(Splitter.fixedLength(sepLength).split(encoded))); 432 } 433 } 434 } 435 testEncoding(BaseEncoding encoding, String decoded, String encoded)436 private static void testEncoding(BaseEncoding encoding, String decoded, String encoded) { 437 testEncodes(encoding, decoded, encoded); 438 testDecodes(encoding, encoded, decoded); 439 } 440 testEncodes(BaseEncoding encoding, String decoded, String encoded)441 private static void testEncodes(BaseEncoding encoding, String decoded, String encoded) { 442 assertThat(encoding.encode(decoded.getBytes(UTF_8))).isEqualTo(encoded); 443 } 444 testEncodesWithOffset( BaseEncoding encoding, String decoded, int offset, int len, String encoded)445 private static void testEncodesWithOffset( 446 BaseEncoding encoding, String decoded, int offset, int len, String encoded) { 447 assertThat(encoding.encode(decoded.getBytes(UTF_8), offset, len)).isEqualTo(encoded); 448 } 449 testDecodes(BaseEncoding encoding, String encoded, String decoded)450 private static void testDecodes(BaseEncoding encoding, String encoded, String decoded) { 451 assertThat(encoding.canDecode(encoded)).isTrue(); 452 assertThat(encoding.decode(encoded)).isEqualTo(decoded.getBytes(UTF_8)); 453 } 454 testDecodesByBytes(BaseEncoding encoding, String encoded, byte[] decoded)455 private static void testDecodesByBytes(BaseEncoding encoding, String encoded, byte[] decoded) { 456 assertThat(encoding.canDecode(encoded)).isTrue(); 457 assertThat(encoding.decode(encoded)).isEqualTo(decoded); 458 } 459 assertFailsToDecode(BaseEncoding encoding, String cannotDecode)460 private static void assertFailsToDecode(BaseEncoding encoding, String cannotDecode) { 461 assertFailsToDecode(encoding, cannotDecode, null); 462 } 463 assertFailsToDecode( BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage)464 private static void assertFailsToDecode( 465 BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) { 466 // We use this somewhat weird pattern with an enum for each assertion we want to make as a way 467 // of dealing with the fact that one of the assertions is @GwtIncompatible but we don't want to 468 // have to have duplicate @GwtIncompatible test methods just to make that assertion. 469 for (AssertFailsToDecodeStrategy strategy : AssertFailsToDecodeStrategy.values()) { 470 strategy.assertFailsToDecode(encoding, cannotDecode, expectedMessage); 471 } 472 } 473 474 enum AssertFailsToDecodeStrategy { 475 @GwtIncompatible // decodingStream(Reader) 476 DECODING_STREAM { 477 @Override assertFailsToDecode( BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage)478 void assertFailsToDecode( 479 BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) { 480 // Regression test for case where DecodingException was swallowed by default implementation 481 // of 482 // InputStream.read(byte[], int, int) 483 // See https://github.com/google/guava/issues/3542 484 Reader reader = new StringReader(cannotDecode); 485 InputStream decodingStream = encoding.decodingStream(reader); 486 try { 487 ByteStreams.exhaust(decodingStream); 488 fail("Expected DecodingException"); 489 } catch (DecodingException expected) { 490 // Don't assert on the expectedMessage; the messages for exceptions thrown from the 491 // decoding stream may differ from the messages for the decode methods. 492 } catch (IOException e) { 493 fail("Expected DecodingException but got: " + e); 494 } 495 } 496 }, 497 CAN_DECODE { 498 @Override assertFailsToDecode( BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage)499 void assertFailsToDecode( 500 BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) { 501 assertThat(encoding.canDecode(cannotDecode)).isFalse(); 502 } 503 }, 504 DECODE { 505 @Override assertFailsToDecode( BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage)506 void assertFailsToDecode( 507 BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) { 508 try { 509 encoding.decode(cannotDecode); 510 fail("Expected IllegalArgumentException"); 511 } catch (IllegalArgumentException expected) { 512 if (expectedMessage != null) { 513 assertThat(expected).hasCauseThat().hasMessageThat().isEqualTo(expectedMessage); 514 } 515 } 516 } 517 }, 518 DECODE_CHECKED { 519 @Override assertFailsToDecode( BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage)520 void assertFailsToDecode( 521 BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage) { 522 try { 523 encoding.decodeChecked(cannotDecode); 524 fail("Expected DecodingException"); 525 } catch (DecodingException expected) { 526 if (expectedMessage != null) { 527 assertThat(expected).hasMessageThat().isEqualTo(expectedMessage); 528 } 529 } 530 } 531 }; 532 assertFailsToDecode( BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage)533 abstract void assertFailsToDecode( 534 BaseEncoding encoding, String cannotDecode, @Nullable String expectedMessage); 535 } 536 537 @GwtIncompatible // Reader/Writer testStreamingEncodingWithCasing( BaseEncoding encoding, String decoded, String encoded)538 private static void testStreamingEncodingWithCasing( 539 BaseEncoding encoding, String decoded, String encoded) throws IOException { 540 testStreamingEncodingWithSeparators(encoding, decoded, encoded); 541 testStreamingEncodingWithSeparators(encoding.upperCase(), decoded, Ascii.toUpperCase(encoded)); 542 testStreamingEncodingWithSeparators(encoding.lowerCase(), decoded, Ascii.toLowerCase(encoded)); 543 } 544 545 @GwtIncompatible // Reader/Writer testStreamingEncodingWithSeparators( BaseEncoding encoding, String decoded, String encoded)546 private static void testStreamingEncodingWithSeparators( 547 BaseEncoding encoding, String decoded, String encoded) throws IOException { 548 testStreamingEncoding(encoding, decoded, encoded); 549 550 // test separators work 551 for (int sepLength = 3; sepLength <= 5; sepLength++) { 552 for (String separator : ImmutableList.of(",", "\n", ";;", "")) { 553 testStreamingEncoding( 554 encoding.withSeparator(separator, sepLength), 555 decoded, 556 Joiner.on(separator).join(Splitter.fixedLength(sepLength).split(encoded))); 557 } 558 } 559 } 560 561 @GwtIncompatible // Reader/Writer testStreamingEncoding(BaseEncoding encoding, String decoded, String encoded)562 private static void testStreamingEncoding(BaseEncoding encoding, String decoded, String encoded) 563 throws IOException { 564 testStreamingEncodes(encoding, decoded, encoded); 565 testStreamingDecodes(encoding, encoded, decoded); 566 } 567 568 @GwtIncompatible // Writer testStreamingEncodes(BaseEncoding encoding, String decoded, String encoded)569 private static void testStreamingEncodes(BaseEncoding encoding, String decoded, String encoded) 570 throws IOException { 571 StringWriter writer = new StringWriter(); 572 try (OutputStream encodingStream = encoding.encodingStream(writer)) { 573 encodingStream.write(decoded.getBytes(UTF_8)); 574 } 575 assertThat(writer.toString()).isEqualTo(encoded); 576 } 577 578 @GwtIncompatible // Reader testStreamingDecodes(BaseEncoding encoding, String encoded, String decoded)579 private static void testStreamingDecodes(BaseEncoding encoding, String encoded, String decoded) 580 throws IOException { 581 byte[] bytes = decoded.getBytes(UTF_8); 582 try (InputStream decodingStream = encoding.decodingStream(new StringReader(encoded))) { 583 for (int i = 0; i < bytes.length; i++) { 584 assertThat(decodingStream.read()).isEqualTo(bytes[i] & 0xFF); 585 } 586 assertThat(decodingStream.read()).isEqualTo(-1); 587 } 588 } 589 testToString()590 public void testToString() { 591 assertThat(base64().toString()).isEqualTo("BaseEncoding.base64().withPadChar('=')"); 592 assertThat(base32Hex().omitPadding().toString()) 593 .isEqualTo("BaseEncoding.base32Hex().omitPadding()"); 594 assertThat(base32().lowerCase().withPadChar('$').toString()) 595 .isEqualTo("BaseEncoding.base32().lowerCase().withPadChar('$')"); 596 assertThat(base16().withSeparator("\n", 10).toString()) 597 .isEqualTo("BaseEncoding.base16().withSeparator(\"\n\", 10)"); 598 } 599 } 600