| [case testAllBase64Features_librt_experimental] |
| from typing import Any |
| import base64 |
| import binascii |
| import random |
| |
| from librt.base64 import b64encode, b64decode, urlsafe_b64encode, urlsafe_b64decode |
| |
| from testutil import assertRaises |
| |
| rand_values = [random.randbytes(random.randint(1, 2000)) for _ in range(2000)] |
| |
| def test_encode_basic() -> None: |
| assert b64encode(b"x") == b"eA==" |
| |
| with assertRaises(TypeError): |
| b64encode(bytearray(b"x")) |
| |
| def check_encode(b: bytes) -> None: |
| assert b64encode(b) == getattr(base64, "b64encode")(b) |
| |
| def test_encode_different_strings() -> None: |
| for i in range(256): |
| check_encode(bytes([i])) |
| check_encode(bytes([i]) + b"x") |
| check_encode(bytes([i]) + b"xy") |
| check_encode(bytes([i]) + b"xyz") |
| check_encode(bytes([i]) + b"xyza") |
| check_encode(b"x" + bytes([i])) |
| check_encode(b"xy" + bytes([i])) |
| check_encode(b"xyz" + bytes([i])) |
| check_encode(b"xyza" + bytes([i])) |
| |
| b = b"a\x00\xb7" * 1000 |
| for i in range(1000): |
| check_encode(b[:i]) |
| |
| for b in b"", b"ab", b"bac", b"1234", b"xyz88", b"abc" * 200: |
| check_encode(b) |
| |
| for b in rand_values: |
| check_encode(b) |
| |
| def test_encode_wrappers() -> None: |
| funcs: list[Any] = [b64encode, urlsafe_b64encode] |
| for enc in funcs: |
| assert enc(b"x") == b"eA==" |
| |
| with assertRaises(TypeError): |
| enc() |
| |
| with assertRaises(TypeError): |
| enc(b"x", b"y") |
| |
| def test_decode_basic() -> None: |
| assert b64decode(b"eA==") == b"x" |
| |
| with assertRaises(TypeError): |
| b64decode(bytearray(b"eA==")) |
| |
| for non_ascii in "\x80", "foo\u100bar", "foo\ua1234bar": |
| with assertRaises(ValueError): |
| b64decode(non_ascii) |
| |
| def check_decode(b: bytes, encoded: bool = False) -> None: |
| if encoded: |
| enc = b |
| else: |
| enc = b64encode(b) |
| assert b64decode(enc) == getattr(base64, "b64decode")(enc) |
| if getattr(enc, "isascii")(): # Test stub has no "isascii" |
| enc_str = enc.decode("ascii") |
| assert b64decode(enc_str) == getattr(base64, "b64decode")(enc_str) |
| |
| def test_decode_different_strings() -> None: |
| for i in range(256): |
| check_decode(bytes([i])) |
| check_decode(bytes([i]) + b"x") |
| check_decode(bytes([i]) + b"xy") |
| check_decode(bytes([i]) + b"xyz") |
| check_decode(bytes([i]) + b"xyza") |
| check_decode(b"x" + bytes([i])) |
| check_decode(b"xy" + bytes([i])) |
| check_decode(b"xyz" + bytes([i])) |
| check_decode(b"xyza" + bytes([i])) |
| |
| b = b"a\x00\xb7" * 1000 |
| for i in range(1000): |
| check_decode(b[:i]) |
| |
| for b in b"", b"ab", b"bac", b"1234", b"xyz88", b"abc" * 200: |
| check_decode(b) |
| |
| for b in rand_values: |
| check_decode(b) |
| |
| def is_base64_char(x: int) -> bool: |
| c = chr(x) |
| return ('a' <= c <= 'z') or ('A' <= c <= 'Z') or ('0' <= c <= '9') or c in '+/=' |
| |
| def test_decode_with_non_base64_chars() -> None: |
| # For stdlib compatibility, non-base64 characters should be ignored. |
| |
| # Invalid characters as a suffix use a fast path. |
| check_decode(b"eA== ", encoded=True) |
| check_decode(b"eA==\n", encoded=True) |
| check_decode(b"eA== \t\n", encoded=True) |
| check_decode(b"\n", encoded=True) |
| |
| check_decode(b" e A = = ", encoded=True) |
| |
| # Special case: Two different encodings of the same data |
| check_decode(b"eAa=", encoded=True) |
| check_decode(b"eAY=", encoded=True) |
| |
| for x in range(256): |
| if not is_base64_char(x): |
| b = bytes([x]) |
| check_decode(b, encoded=True) |
| check_decode(b"eA==" + b, encoded=True) |
| check_decode(b"e" + b + b"A==", encoded=True) |
| check_decode(b"eA=" + b + b"=", encoded=True) |
| |
| def check_decode_error(b: bytes, ignore_stdlib: bool = False) -> None: |
| if not ignore_stdlib: |
| with assertRaises(binascii.Error): |
| getattr(base64, "b64decode")(b) |
| |
| # The raised error is different, since librt shouldn't depend on binascii |
| with assertRaises(ValueError): |
| b64decode(b) |
| |
| def test_decode_with_invalid_padding() -> None: |
| check_decode_error(b"eA") |
| check_decode_error(b"eA=") |
| check_decode_error(b"eHk") |
| check_decode_error(b"eA = ") |
| |
| # Here stdlib behavior seems nonsensical, so we don't try to duplicate it |
| check_decode_error(b"eA=a=", ignore_stdlib=True) |
| |
| def test_decode_with_extra_data_after_padding() -> None: |
| check_decode(b"=", encoded=True) |
| check_decode(b"==", encoded=True) |
| check_decode(b"===", encoded=True) |
| check_decode(b"====", encoded=True) |
| check_decode(b"eA===", encoded=True) |
| check_decode(b"eHk==", encoded=True) |
| check_decode(b"eA==x", encoded=True) |
| check_decode(b"eHk=x", encoded=True) |
| check_decode(b"eA==abc=======efg", encoded=True) |
| |
| def test_decode_wrappers() -> None: |
| funcs: list[Any] = [b64decode, urlsafe_b64decode] |
| for dec in funcs: |
| assert dec(b"eA==") == b"x" |
| |
| with assertRaises(TypeError): |
| dec() |
| |
| with assertRaises(TypeError): |
| dec(b"x", b"y") |
| |
| def check_urlsafe_encode(b: bytes) -> None: |
| assert urlsafe_b64encode(b) == getattr(base64, "urlsafe_b64encode")(b) |
| |
| def test_urlsafe_b64encode() -> None: |
| check_urlsafe_encode(b"") |
| check_urlsafe_encode(b"a") |
| check_urlsafe_encode(b"\xf8") |
| check_urlsafe_encode(b"\xfc") |
| check_urlsafe_encode(b"\xfcx") |
| check_urlsafe_encode(b"\xfcxy") |
| check_urlsafe_encode(b"\xfcxyz") |
| check_urlsafe_encode(bytes([x for x in range(256)])) |
| |
| for b in rand_values: |
| check_urlsafe_encode(b) |
| |
| def check_urlsafe_decode(b: bytes) -> None: |
| enc = urlsafe_b64encode(b) |
| assert urlsafe_b64decode(enc) == getattr(base64, "urlsafe_b64decode")(enc) |
| enc2 = b64encode(b) |
| assert urlsafe_b64decode(enc2) == getattr(base64, "urlsafe_b64decode")(enc2) |
| |
| def test_urlsafe_b64decode() -> None: |
| # Don't test everything, since the implementation is mostly shared with b64decode. |
| check_urlsafe_decode(b"") |
| check_urlsafe_decode(b"a") |
| check_urlsafe_decode(b"\xf8") |
| check_urlsafe_decode(b"\xfc") |
| check_urlsafe_decode(b"\xfcx") |
| check_urlsafe_decode(b"\xfcxy") |
| check_urlsafe_decode(b"\xfcxyz") |
| check_urlsafe_decode(bytes([x for x in range(256)])) |
| |
| for b in rand_values: |
| check_urlsafe_decode(b) |
| |
| assert urlsafe_b64decode(b" e A = == !") == b"x" |
| |
| def test_urlsafe_b64decode_errors() -> None: |
| for b in b"eA", b"eA=", b"eHk": |
| with assertRaises(ValueError): |
| b64decode(b) |
| |
| [case testBase64FeaturesNotAvailableInNonExperimentalBuild_librt_base64] |
| # This also ensures librt.base64 can be built without experimental features |
| import librt.base64 |
| |
| def test_b64encode_not_available() -> None: |
| assert not hasattr(librt.base64, "b64encode") |
| |
| [case testBase64UsedAtTopLevelOnly_librt_experimental] |
| from librt.base64 import b64encode |
| |
| # The only reference to b64encode is at module top level |
| encoded = b64encode(b"x") |
| |
| def test_top_level_only_encode() -> None: |
| assert encoded == b"eA==" |