| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| |
| #include "iceberg/util/base64.h" |
| |
| #include <gtest/gtest.h> |
| |
| #include "iceberg/test/matchers.h" |
| |
| namespace iceberg { |
| |
| TEST(Base64Test, Encode) { |
| // Empty string |
| EXPECT_EQ("", Base64::Encode("")); |
| |
| // Single character |
| EXPECT_EQ("YQ==", Base64::Encode("a")); |
| EXPECT_EQ("YWI=", Base64::Encode("ab")); |
| EXPECT_EQ("YWJj", Base64::Encode("abc")); |
| |
| // Multiple of 3 characters |
| EXPECT_EQ("YWJjZGU=", Base64::Encode("abcde")); |
| EXPECT_EQ("YWJjZGVm", Base64::Encode("abcdef")); |
| |
| // Common strings |
| EXPECT_EQ("U29tZSBkYXRhIHdpdGggY2hhcmFjdGVycw==", |
| Base64::Encode("Some data with characters")); |
| EXPECT_EQ("aGVsbG8=", Base64::Encode("hello")); |
| EXPECT_EQ("dGVzdCBzdHJpbmc=", Base64::Encode("test string")); |
| |
| // Unicode |
| EXPECT_EQ("8J+EgA==", Base64::Encode("\xF0\x9F\x84\x80")); |
| // Null byte |
| EXPECT_EQ("AA==", Base64::Encode({"\x00", 1})); |
| } |
| |
| TEST(Base64Test, Decode) { |
| // Empty string |
| ICEBERG_UNWRAP_OR_FAIL(auto empty, Base64::Decode("")); |
| EXPECT_EQ("", empty); |
| |
| // Round-trip with Base64::Encode |
| ICEBERG_UNWRAP_OR_FAIL(auto a, Base64::Decode("YQ==")); |
| EXPECT_EQ("a", a); |
| ICEBERG_UNWRAP_OR_FAIL(auto ab, Base64::Decode("YWI=")); |
| EXPECT_EQ("ab", ab); |
| ICEBERG_UNWRAP_OR_FAIL(auto abc, Base64::Decode("YWJj")); |
| EXPECT_EQ("abc", abc); |
| ICEBERG_UNWRAP_OR_FAIL(auto abcde, Base64::Decode("YWJjZGU=")); |
| EXPECT_EQ("abcde", abcde); |
| ICEBERG_UNWRAP_OR_FAIL(auto abcdef, Base64::Decode("YWJjZGVm")); |
| EXPECT_EQ("abcdef", abcdef); |
| ICEBERG_UNWRAP_OR_FAIL(auto hello, Base64::Decode("aGVsbG8=")); |
| EXPECT_EQ("hello", hello); |
| ICEBERG_UNWRAP_OR_FAIL(auto test_str, Base64::Decode("dGVzdCBzdHJpbmc=")); |
| EXPECT_EQ("test string", test_str); |
| |
| // Without padding (should still work) |
| ICEBERG_UNWRAP_OR_FAIL(auto a2, Base64::Decode("YQ")); |
| EXPECT_EQ("a", a2); |
| ICEBERG_UNWRAP_OR_FAIL(auto ab2, Base64::Decode("YWI")); |
| EXPECT_EQ("ab", ab2); |
| |
| // Invalid characters return error |
| EXPECT_THAT(Base64::Decode("!!!"), IsError(ErrorKind::kInvalidArgument)); |
| |
| // Invalid padding and impossible unpadded lengths return error |
| for (const auto* invalid : |
| {"=", "====", "Y=Q=", "YQ=", "YQ===", "YWJj=", "aGVsbG8==", "A", "AAAAA"}) { |
| EXPECT_THAT(Base64::Decode(invalid), IsError(ErrorKind::kInvalidArgument)) << invalid; |
| } |
| } |
| |
| TEST(Base64Test, UrlEncode) { |
| // Empty string |
| EXPECT_EQ("", Base64::UrlEncode("")); |
| |
| // No padding is emitted (unlike standard base64) |
| EXPECT_EQ("YQ", Base64::UrlEncode("a")); |
| EXPECT_EQ("YWI", Base64::UrlEncode("ab")); |
| EXPECT_EQ("YWJj", Base64::UrlEncode("abc")); |
| EXPECT_EQ("aGVsbG8", Base64::UrlEncode("hello")); |
| |
| // URL-safe characters: '-' and '_' instead of '+' and '/' |
| // bytes {0xFB, 0xFF, 0xFE} encode to "+//+" in standard base64, "-__-" in base64url |
| EXPECT_EQ("-__-", Base64::UrlEncode("\xFB\xFF\xFE")); |
| |
| // Round-trip with UrlDecode |
| ICEBERG_UNWRAP_OR_FAIL(auto decoded, Base64::UrlDecode(Base64::UrlEncode("hello"))); |
| EXPECT_EQ("hello", decoded); |
| } |
| |
| TEST(Base64Test, UrlDecode) { |
| // Empty string |
| ICEBERG_UNWRAP_OR_FAIL(auto empty, Base64::UrlDecode("")); |
| EXPECT_EQ("", empty); |
| |
| // Standard cases (same as Base64::Decode for alphanumeric) |
| ICEBERG_UNWRAP_OR_FAIL(auto hello, Base64::UrlDecode("aGVsbG8")); |
| EXPECT_EQ("hello", hello); |
| ICEBERG_UNWRAP_OR_FAIL(auto abc, Base64::UrlDecode("YWJj")); |
| EXPECT_EQ("abc", abc); |
| |
| // URL-safe characters: '-' and '_' instead of '+' and '/' |
| // bytes {0xFB, 0xFF, 0xFE} encode to "+//+" in standard base64, "-__-" in base64url |
| ICEBERG_UNWRAP_OR_FAIL(auto decoded, Base64::UrlDecode("-__-")); |
| EXPECT_EQ(3u, decoded.size()); |
| EXPECT_EQ('\xFB', decoded[0]); |
| EXPECT_EQ('\xFF', decoded[1]); |
| EXPECT_EQ('\xFE', decoded[2]); |
| |
| // Standard base64 chars '+' and '/' should be invalid in base64url |
| EXPECT_THAT(Base64::UrlDecode("+//+"), IsError(ErrorKind::kInvalidArgument)); |
| |
| // With padding (should handle gracefully) |
| ICEBERG_UNWRAP_OR_FAIL(auto hello2, Base64::UrlDecode("aGVsbG8=")); |
| EXPECT_EQ("hello", hello2); |
| |
| // Invalid characters return error |
| EXPECT_THAT(Base64::UrlDecode("!!!invalid!!!"), IsError(ErrorKind::kInvalidArgument)); |
| |
| // Invalid padding and impossible unpadded lengths return error |
| for (const auto* invalid : |
| {"=", "====", "Y=Q=", "YQ=", "YQ===", "YWJj=", "aGVsbG8==", "A", "AAAAA"}) { |
| EXPECT_THAT(Base64::UrlDecode(invalid), IsError(ErrorKind::kInvalidArgument)) |
| << invalid; |
| } |
| } |
| |
| } // namespace iceberg |