1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_tokenizer/base64.h"
16 
17 #include <cstring>
18 #include <span>
19 #include <string_view>
20 
21 #include "gtest/gtest.h"
22 
23 namespace pw::tokenizer {
24 namespace {
25 
26 using std::byte;
27 
28 class PrefixedBase64 : public ::testing::Test {
29  protected:
30   static constexpr char kUnset = '#';
31 
PrefixedBase64()32   PrefixedBase64() {
33     std::memset(binary_, kUnset, sizeof(binary_));
34     std::memset(base64_, kUnset, sizeof(base64_));
35   }
36 
37   byte binary_[32];
38   char base64_[32];
39 };
40 
41 const struct TestData {
42   template <size_t kSize>
TestDatapw::tokenizer::__anone66a302d0111::TestData43   TestData(const char (&binary_data)[kSize], const char* base64_data)
44       : binary{std::as_bytes(std::span(binary_data, kSize - 1))},
45         base64(base64_data) {}
46 
47   std::span<const byte> binary;
48   std::string_view base64;
49 } kTestData[] = {
50     {"", "$"},
51     {"\x00", "$AA=="},
52     {"\x71", "$cQ=="},
53     {"\xff", "$/w=="},
54     {"\x63\xa9", "$Y6k="},
55     {"\x69\x89\x03", "$aYkD"},
56     {"\x80\xf5\xc8\xd4", "$gPXI1A=="},
57     {"\x6e\xb8\x91\x3f\xac", "$briRP6w="},
58     {"\x1f\x88\x91\xbb\xd7\x10", "$H4iRu9cQ"},
59     {"\xac\xcf\xb2\xd5\xee\xa2\x8e", "$rM+y1e6ijg=="},
60     {"\xff\x15\x25\x7e\x7b\xc9\x7b\x60", "$/xUlfnvJe2A="},
61     {"\xd5\xab\xd9\xa6\xae\xaa\x33\x9f\x66", "$1avZpq6qM59m"},
62     {"\x6b\xfd\x95\xc5\x4a\xc7\xc2\x39\x45\xdc", "$a/2VxUrHwjlF3A=="},
63     {"\x4c\xde\xee\xb8\x68\x0d\x9c\x66\x3e\xea\x46", "$TN7uuGgNnGY+6kY="},
64 };
65 
TEST_F(PrefixedBase64,Encode)66 TEST_F(PrefixedBase64, Encode) {
67   for (auto& [binary, base64] : kTestData) {
68     EXPECT_EQ(base64.size(), PrefixedBase64Encode(binary, base64_));
69     ASSERT_EQ(base64, base64_);
70     EXPECT_EQ('\0', base64_[base64.size()]);
71   }
72 }
73 
TEST_F(PrefixedBase64,Encode_EmptyInput_WritesPrefix)74 TEST_F(PrefixedBase64, Encode_EmptyInput_WritesPrefix) {
75   EXPECT_EQ(1u, PrefixedBase64Encode(std::span<byte>(), base64_));
76   EXPECT_EQ('$', base64_[0]);
77   EXPECT_EQ('\0', base64_[1]);
78 }
79 
TEST_F(PrefixedBase64,Encode_EmptyOutput_WritesNothing)80 TEST_F(PrefixedBase64, Encode_EmptyOutput_WritesNothing) {
81   EXPECT_EQ(0u,
82             PrefixedBase64Encode(kTestData[5].binary, std::span(base64_, 0)));
83   EXPECT_EQ(kUnset, base64_[0]);
84 }
85 
TEST_F(PrefixedBase64,Encode_SingleByteOutput_OnlyNullTerminates)86 TEST_F(PrefixedBase64, Encode_SingleByteOutput_OnlyNullTerminates) {
87   EXPECT_EQ(0u,
88             PrefixedBase64Encode(kTestData[5].binary, std::span(base64_, 1)));
89   EXPECT_EQ('\0', base64_[0]);
90   EXPECT_EQ(kUnset, base64_[1]);
91 }
92 
TEST_F(PrefixedBase64,Encode_NoRoomForNullAfterMessage_OnlyNullTerminates)93 TEST_F(PrefixedBase64, Encode_NoRoomForNullAfterMessage_OnlyNullTerminates) {
94   EXPECT_EQ(
95       0u,
96       PrefixedBase64Encode(kTestData[5].binary,
97                            std::span(base64_, kTestData[5].base64.size())));
98   EXPECT_EQ('\0', base64_[0]);
99   EXPECT_EQ(kUnset, base64_[1]);
100 }
101 
TEST_F(PrefixedBase64,Base64EncodedBufferSize_Empty_RoomForPrefixAndNull)102 TEST_F(PrefixedBase64, Base64EncodedBufferSize_Empty_RoomForPrefixAndNull) {
103   EXPECT_EQ(2u, Base64EncodedBufferSize(0));
104 }
105 
TEST_F(PrefixedBase64,Base64EncodedBufferSize_PositiveSizes)106 TEST_F(PrefixedBase64, Base64EncodedBufferSize_PositiveSizes) {
107   for (unsigned i = 1; i <= 3; ++i) {
108     EXPECT_EQ(6u, Base64EncodedBufferSize(i));
109   }
110   for (unsigned i = 4; i <= 6; ++i) {
111     EXPECT_EQ(10u, Base64EncodedBufferSize(i));
112   }
113 }
114 
TEST_F(PrefixedBase64,Decode)115 TEST_F(PrefixedBase64, Decode) {
116   for (auto& [binary, base64] : kTestData) {
117     EXPECT_EQ(binary.size(), PrefixedBase64Decode(base64, binary_));
118     ASSERT_EQ(0, std::memcmp(binary.data(), binary_, binary.size()));
119   }
120 }
121 
TEST_F(PrefixedBase64,Decode_EmptyInput_WritesNothing)122 TEST_F(PrefixedBase64, Decode_EmptyInput_WritesNothing) {
123   EXPECT_EQ(0u, PrefixedBase64Decode({}, binary_));
124   EXPECT_EQ(byte{kUnset}, binary_[0]);
125 }
126 
TEST_F(PrefixedBase64,Decode_OnlyPrefix_WritesNothing)127 TEST_F(PrefixedBase64, Decode_OnlyPrefix_WritesNothing) {
128   EXPECT_EQ(0u, PrefixedBase64Decode("$", binary_));
129   EXPECT_EQ(byte{kUnset}, binary_[0]);
130 }
131 
TEST_F(PrefixedBase64,Decode_EmptyOutput_WritesNothing)132 TEST_F(PrefixedBase64, Decode_EmptyOutput_WritesNothing) {
133   EXPECT_EQ(0u,
134             PrefixedBase64Decode(kTestData[5].base64, std::span(binary_, 0)));
135   EXPECT_EQ(byte{kUnset}, binary_[0]);
136 }
137 
TEST_F(PrefixedBase64,Decode_OutputTooSmall_WritesNothing)138 TEST_F(PrefixedBase64, Decode_OutputTooSmall_WritesNothing) {
139   auto& item = kTestData[5];
140   EXPECT_EQ(0u,
141             PrefixedBase64Decode(item.base64,
142                                  std::span(binary_, item.binary.size() - 1)));
143   EXPECT_EQ(byte{kUnset}, binary_[0]);
144 }
145 
TEST(PrefixedBase64,DecodeInPlace)146 TEST(PrefixedBase64, DecodeInPlace) {
147   byte buffer[32];
148 
149   for (auto& [binary, base64] : kTestData) {
150     std::memcpy(buffer, base64.data(), base64.size());
151 
152     EXPECT_EQ(binary.size(),
153               PrefixedBase64DecodeInPlace(std::span(buffer, base64.size())));
154     ASSERT_EQ(0, std::memcmp(binary.data(), buffer, binary.size()));
155   }
156 }
157 
158 }  // namespace
159 }  // namespace pw::tokenizer
160