xref: /aosp_15_r20/external/pigweed/pw_protobuf/map_utils_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2021 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_protobuf/map_utils.h"
16 
17 #include <string_view>
18 
19 #include "pw_stream/memory_stream.h"
20 #include "pw_stream/stream.h"
21 #include "pw_unit_test/framework.h"
22 
23 namespace pw::protobuf {
24 namespace {
25 
TEST(ProtoHelper,WriteProtoStringToBytesMapEntry)26 TEST(ProtoHelper, WriteProtoStringToBytesMapEntry) {
27   // The following defines an instance of the message below:
28   //
29   // message Maps {
30   //   map<string, string> map_a = 1;
31   //   map<string, string> map_b = 2;
32   // }
33   //
34   // where
35   //
36   // Maps.map_a['key_foo'] = 'foo_a'
37   // Maps.map_a['key_bar'] = 'bar_a'
38   //
39   // Maps.map_b['key_foo'] = 'foo_b'
40   // Maps.map_b['key_bar'] = 'bar_b'
41   //
42   // clang-format off
43   std::uint8_t encoded_proto[] = {
44     // map_a["key_bar"] = "bar_a", key = 1
45     0x0a, 0x10,
46     0x0a, 0x07, 'k', 'e', 'y', '_', 'b', 'a', 'r', // map key
47     0x12, 0x05, 'b', 'a', 'r', '_', 'a', // map value
48 
49     // map_a["key_foo"] = "foo_a", key = 1
50     0x0a, 0x10,
51     0x0a, 0x07, 'k', 'e', 'y', '_', 'f', 'o', 'o',
52     0x12, 0x05, 'f', 'o', 'o', '_', 'a',
53 
54     // map_b["key_foo"] = "foo_b", key = 2
55     0x12, 0x10,
56     0x0a, 0x07, 'k', 'e', 'y', '_', 'f', 'o', 'o',
57     0x12, 0x05, 'f', 'o', 'o', '_', 'b',
58 
59     // map_b["key_bar"] = "bar_b", key = 2
60     0x12, 0x10,
61     0x0a, 0x07, 'k', 'e', 'y', '_', 'b', 'a', 'r',
62     0x12, 0x05, 'b', 'a', 'r', '_', 'b',
63   };
64   // clang-format on
65 
66   // Now construct the same message with WriteStringToBytesMapEntry
67   std::byte dst_buffer[sizeof(encoded_proto)];
68   stream::MemoryWriter writer(dst_buffer);
69 
70   const struct {
71     uint32_t field_number;
72     std::string_view key;
73     std::string_view value;
74   } kMapData[] = {
75       {1, "key_bar", "bar_a"},
76       {1, "key_foo", "foo_a"},
77       {2, "key_foo", "foo_b"},
78       {2, "key_bar", "bar_b"},
79   };
80 
81   std::byte stream_pipe_buffer[1];
82   for (auto ele : kMapData) {
83     stream::MemoryReader key_reader(as_bytes(span<const char>{ele.key}));
84     stream::MemoryReader value_reader(as_bytes(span<const char>{ele.value}));
85     PW_TEST_ASSERT_OK(WriteProtoStringToBytesMapEntry(ele.field_number,
86                                                       key_reader,
87                                                       ele.key.size(),
88                                                       value_reader,
89                                                       ele.value.size(),
90                                                       stream_pipe_buffer,
91                                                       writer));
92   }
93 
94   ASSERT_EQ(memcmp(dst_buffer, encoded_proto, sizeof(dst_buffer)), 0);
95 }
96 
TEST(ProtoHelper,WriteProtoStringToBytesMapEntryExceedsWriteLimit)97 TEST(ProtoHelper, WriteProtoStringToBytesMapEntryExceedsWriteLimit) {
98   // Construct an instance of the message below:
99   //
100   // message Maps {
101   //   map<string, string> map_a = 1;
102   // }
103   //
104   // where
105   //
106   // Maps.map_a['key_bar'] = 'bar_a'. The needed buffer size is 18 in this
107   // case:
108   //
109   // {
110   //   0x0a, 0x10,
111   //   0x0a, 0x07, 'k', 'e', 'y', '_', 'b', 'a', 'r',
112   //   0x12, 0x05, 'b', 'a', 'r', '_', 'a',
113   // }
114   //
115   // Use a smaller buffer.
116   std::byte encode_buffer[17];
117   stream::MemoryWriter writer(encode_buffer);
118   constexpr uint32_t kFieldNumber = 1;
119   std::string_view key = "key_bar";
120   std::string_view value = "bar_a";
121   stream::MemoryReader key_reader(as_bytes(span<const char>{key}));
122   stream::MemoryReader value_reader(as_bytes(span<const char>{value}));
123   std::byte stream_pipe_buffer[1];
124   ASSERT_EQ(
125       WriteProtoStringToBytesMapEntry(kFieldNumber,
126                                       key_reader,
127                                       key_reader.ConservativeReadLimit(),
128                                       value_reader,
129                                       value_reader.ConservativeReadLimit(),
130                                       stream_pipe_buffer,
131                                       writer),
132       Status::ResourceExhausted());
133 }
134 
TEST(ProtoHelper,WriteProtoStringToBytesMapEntryInvalidArgument)135 TEST(ProtoHelper, WriteProtoStringToBytesMapEntryInvalidArgument) {
136   std::byte encode_buffer[17];
137   stream::MemoryWriter writer(encode_buffer);
138   std::string_view key = "key_bar";
139   std::string_view value = "bar_a";
140   stream::MemoryReader key_reader(as_bytes(span<const char>{key}));
141   stream::MemoryReader value_reader(as_bytes(span<const char>{value}));
142   std::byte stream_pipe_buffer[1];
143 
144   ASSERT_EQ(
145       WriteProtoStringToBytesMapEntry(19091,
146                                       key_reader,
147                                       key_reader.ConservativeReadLimit(),
148                                       value_reader,
149                                       value_reader.ConservativeReadLimit(),
150                                       stream_pipe_buffer,
151                                       writer),
152       Status::InvalidArgument());
153 }
154 
155 }  // namespace
156 }  // namespace pw::protobuf
157