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 <string_view>
19
20 #include "pw_span/span.h"
21 #include "pw_unit_test/framework.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::__anonfddee4630111::TestData43 TestData(const char (&binary_data)[kSize], const char* base64_data)
44 : binary{as_bytes(span(binary_data, kSize - 1))}, base64(base64_data) {}
45
46 span<const byte> binary;
47 std::string_view base64;
48 } kTestData[] = {
49 {"", "$"},
50 {"\x00", "$AA=="},
51 {"\x71", "$cQ=="},
52 {"\xff", "$/w=="},
53 {"\x63\xa9", "$Y6k="},
54 {"\x69\x89\x03", "$aYkD"},
55 {"\x80\xf5\xc8\xd4", "$gPXI1A=="},
56 {"\x6e\xb8\x91\x3f\xac", "$briRP6w="},
57 {"\x1f\x88\x91\xbb\xd7\x10", "$H4iRu9cQ"},
58 {"\xac\xcf\xb2\xd5\xee\xa2\x8e", "$rM+y1e6ijg=="},
59 {"\xff\x15\x25\x7e\x7b\xc9\x7b\x60", "$/xUlfnvJe2A="},
60 {"\xd5\xab\xd9\xa6\xae\xaa\x33\x9f\x66", "$1avZpq6qM59m"},
61 {"\x6b\xfd\x95\xc5\x4a\xc7\xc2\x39\x45\xdc", "$a/2VxUrHwjlF3A=="},
62 {"\x4c\xde\xee\xb8\x68\x0d\x9c\x66\x3e\xea\x46", "$TN7uuGgNnGY+6kY="},
63 };
64
TEST_F(PrefixedBase64,Encode)65 TEST_F(PrefixedBase64, Encode) {
66 for (auto& [binary, base64] : kTestData) {
67 EXPECT_EQ(base64.size(), PrefixedBase64Encode(binary, base64_));
68 ASSERT_EQ(base64, base64_);
69 EXPECT_EQ('\0', base64_[base64.size()]);
70 }
71 }
72
TEST_F(PrefixedBase64,Encode_EmptyInput_WritesPrefix)73 TEST_F(PrefixedBase64, Encode_EmptyInput_WritesPrefix) {
74 EXPECT_EQ(1u, PrefixedBase64Encode(span<byte>(), base64_));
75 EXPECT_EQ('$', base64_[0]);
76 EXPECT_EQ('\0', base64_[1]);
77 }
78
TEST_F(PrefixedBase64,Encode_EmptyOutput_WritesNothing)79 TEST_F(PrefixedBase64, Encode_EmptyOutput_WritesNothing) {
80 EXPECT_EQ(0u, PrefixedBase64Encode(kTestData[5].binary, span(base64_, 0)));
81 EXPECT_EQ(kUnset, base64_[0]);
82 }
83
TEST_F(PrefixedBase64,Encode_SingleByteOutput_OnlyNullTerminates)84 TEST_F(PrefixedBase64, Encode_SingleByteOutput_OnlyNullTerminates) {
85 EXPECT_EQ(0u, PrefixedBase64Encode(kTestData[5].binary, span(base64_, 1)));
86 EXPECT_EQ('\0', base64_[0]);
87 EXPECT_EQ(kUnset, base64_[1]);
88 }
89
TEST_F(PrefixedBase64,Encode_NoRoomForNullAfterMessage_OnlyNullTerminates)90 TEST_F(PrefixedBase64, Encode_NoRoomForNullAfterMessage_OnlyNullTerminates) {
91 EXPECT_EQ(0u,
92 PrefixedBase64Encode(kTestData[5].binary,
93 span(base64_, kTestData[5].base64.size())));
94 EXPECT_EQ('\0', base64_[0]);
95 EXPECT_EQ(kUnset, base64_[1]);
96 }
97
TEST_F(PrefixedBase64,Encode_InlineString)98 TEST_F(PrefixedBase64, Encode_InlineString) {
99 for (auto& [binary, base64] : kTestData) {
100 EXPECT_EQ(base64, PrefixedBase64Encode<64>(binary));
101 }
102 }
103
TEST_F(PrefixedBase64,Encode_InlineString_Append)104 TEST_F(PrefixedBase64, Encode_InlineString_Append) {
105 for (auto& [binary, base64] : kTestData) {
106 pw::InlineString<32> string("Other stuff!");
107 PrefixedBase64Encode(binary, string);
108
109 pw::InlineString<32> expected("Other stuff!");
110 expected.append(base64);
111
112 EXPECT_EQ(expected, string);
113 }
114 }
115
TEST_F(PrefixedBase64,Base64EncodedBufferSize_Empty_RoomForPrefixAndNull)116 TEST_F(PrefixedBase64, Base64EncodedBufferSize_Empty_RoomForPrefixAndNull) {
117 EXPECT_EQ(2u, Base64EncodedBufferSize(0));
118 }
119
TEST_F(PrefixedBase64,Base64EncodedBufferSize_PositiveSizes)120 TEST_F(PrefixedBase64, Base64EncodedBufferSize_PositiveSizes) {
121 for (unsigned i = 1; i <= 3; ++i) {
122 EXPECT_EQ(6u, Base64EncodedBufferSize(i));
123 }
124 for (unsigned i = 4; i <= 6; ++i) {
125 EXPECT_EQ(10u, Base64EncodedBufferSize(i));
126 }
127 }
128
TEST_F(PrefixedBase64,Decode)129 TEST_F(PrefixedBase64, Decode) {
130 for (auto& [binary, base64] : kTestData) {
131 EXPECT_EQ(binary.size(), PrefixedBase64Decode(base64, binary_));
132 ASSERT_EQ(0, std::memcmp(binary.data(), binary_, binary.size()));
133 }
134 }
135
TEST_F(PrefixedBase64,Decode_EmptyInput_WritesNothing)136 TEST_F(PrefixedBase64, Decode_EmptyInput_WritesNothing) {
137 EXPECT_EQ(0u, PrefixedBase64Decode({}, binary_));
138 EXPECT_EQ(byte{kUnset}, binary_[0]);
139 }
140
TEST_F(PrefixedBase64,Decode_OnlyPrefix_WritesNothing)141 TEST_F(PrefixedBase64, Decode_OnlyPrefix_WritesNothing) {
142 EXPECT_EQ(0u, PrefixedBase64Decode("$", binary_));
143 EXPECT_EQ(byte{kUnset}, binary_[0]);
144 }
145
TEST_F(PrefixedBase64,Decode_EmptyOutput_WritesNothing)146 TEST_F(PrefixedBase64, Decode_EmptyOutput_WritesNothing) {
147 EXPECT_EQ(0u, PrefixedBase64Decode(kTestData[5].base64, span(binary_, 0)));
148 EXPECT_EQ(byte{kUnset}, binary_[0]);
149 }
150
TEST_F(PrefixedBase64,Decode_OutputTooSmall_WritesNothing)151 TEST_F(PrefixedBase64, Decode_OutputTooSmall_WritesNothing) {
152 auto& item = kTestData[5];
153 EXPECT_EQ(
154 0u,
155 PrefixedBase64Decode(item.base64, span(binary_, item.binary.size() - 1)));
156 EXPECT_EQ(byte{kUnset}, binary_[0]);
157 }
158
TEST(PrefixedBase64DecodeInPlace,DecodeInPlace)159 TEST(PrefixedBase64DecodeInPlace, DecodeInPlace) {
160 byte buffer[32];
161
162 for (auto& [binary, base64] : kTestData) {
163 std::memcpy(buffer, base64.data(), base64.size());
164
165 EXPECT_EQ(binary.size(),
166 PrefixedBase64DecodeInPlace(span(buffer, base64.size())));
167 ASSERT_EQ(0, std::memcmp(binary.data(), buffer, binary.size()));
168 }
169 }
170
171 } // namespace
172 } // namespace pw::tokenizer
173