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