blob: 425a2ca18825a5922f4aae22dbefec50f1e552c5 [file] [log] [blame]
Wyatt Hepler80c6ee52020-01-03 09:54:58 -08001// 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/internal/decode.h"
16
17#include <cstdio>
18#include <string>
19#include <string_view>
20
21#include "gtest/gtest.h"
22#include "pw_tokenizer_private/tokenized_string_decoding_test_data.h"
23#include "pw_tokenizer_private/varint_decoding_test_data.h"
24#include "pw_varint/varint.h"
25
26#define ERR PW_TOKENIZER_ARG_DECODING_ERROR
27
28namespace pw::tokenizer {
29namespace {
30
31using namespace std::literals::string_view_literals;
32
33const FormatString kOneArg("Hello %s");
34const FormatString kTwoArgs("The %d %s");
35
36const bool kSupportsC99Printf = []() {
37 char buf[16] = {};
38 std::snprintf(buf, sizeof(buf), "%zu", sizeof(char));
39 return buf[0] == '1' && buf[1] == '\0';
40}();
41
42const bool kSupportsFloatPrintf = []() {
43 char buf[16] = {};
44 std::snprintf(buf, sizeof(buf), "%.1f", 3.5f);
45 return buf[0] == '3' && buf[1] == '.' && buf[2] == '5' && buf[3] == '\0';
46}();
47
48bool FormatIsSupported(std::string_view value) {
49 return (kSupportsC99Printf || (value.find("%hh") == std::string_view::npos &&
50 value.find("%ll") == std::string_view::npos &&
51 value.find("%j") == std::string_view::npos &&
52 value.find("%z") == std::string_view::npos &&
53 value.find("%t") == std::string_view::npos)) &&
54 // To make absolutely certain there are no floating point numbers, skip
55 // anything with an f or e in it.
56 (kSupportsFloatPrintf || (value.find('f') == std::string_view::npos &&
57 value.find('e') == std::string_view::npos));
58}
59
60TEST(TokenizedStringDecode, TokenizedStringDecodingTestCases) {
61 const auto& test_data = test::tokenized_string_decoding::kTestData;
62 static_assert(sizeof(test_data) / sizeof(*test_data) > 100u);
63
64 size_t skipped = 0;
65
66 for (const auto& [format, expected, args] : test_data) {
67 if (FormatIsSupported(format)) {
68 ASSERT_EQ(FormatString(format).Format(args).value_with_errors(),
69 expected);
70 } else {
71 skipped += 1;
72 }
73 }
74
75 if (sizeof(void*) == 8) { // 64-bit systems should have full snprintf.
76 ASSERT_EQ(skipped, 0u);
77 }
78}
79
80TEST(TokenizedStringDecode, FullyDecodeInput_ZeroRemainingBytes) {
81 auto result = kOneArg.Format("\5hello");
82 EXPECT_EQ(result.value(), "Hello hello");
83 EXPECT_EQ(result.remaining_bytes(), 0u);
84 EXPECT_EQ(result.decoding_errors(), 0u);
85}
86
87TEST(TokenizedStringDecode, PartiallyDecodeInput_HasRemainingBytes) {
88 auto result = kOneArg.Format("\5helloworld");
89 EXPECT_EQ(result.value(), "Hello hello");
90 EXPECT_EQ(result.remaining_bytes(), 5u);
91 EXPECT_EQ(result.decoding_errors(), 0u);
92}
93
94TEST(TokenizedStringDecode, Truncation_NotAnError) {
95 auto result = kTwoArgs.Format("\6\x89musketeer");
96 EXPECT_EQ(result.value(), "The 3 musketeer[...]");
97 EXPECT_EQ(result.value_with_errors(), "The 3 musketeer[...]");
98 EXPECT_EQ(result.remaining_bytes(), 0u);
99 EXPECT_EQ(result.decoding_errors(), 0u);
100}
101
102TEST(TokenizedStringDecode, WrongStringLenth_IsErrorAndConsumesRestOfString) {
103 auto result = kTwoArgs.Format("\6\x0amusketeer");
104 EXPECT_EQ(result.value(), "The 3 %s");
105 EXPECT_EQ(result.value_with_errors(), "The 3 " ERR("%s ERROR (musketeer)"));
106 EXPECT_EQ(result.remaining_bytes(), 0u);
107 EXPECT_EQ(result.decoding_errors(), 1u);
108}
109
110TEST(TokenizedStringDecode, UnterminatedVarint_IsError) {
111 auto result = kTwoArgs.Format("\x80");
112 EXPECT_EQ(result.value(), "The %d %s");
113 EXPECT_EQ(result.value_with_errors(),
114 "The " ERR("%d ERROR") " " ERR("%s SKIPPED"));
115 EXPECT_EQ(result.remaining_bytes(), 0u);
116 EXPECT_EQ(result.decoding_errors(), 2u);
117}
118
119TEST(TokenizedStringDecode, UnterminatedVarint_ConsumesUpToMaxVarintSize) {
120 std::string_view data = "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80";
121 auto result = kTwoArgs.Format(data);
122 EXPECT_EQ(result.value(), "The %d %s");
123 EXPECT_EQ(result.value_with_errors(),
124 "The " ERR("%d ERROR") " " ERR("%s SKIPPED"));
125 EXPECT_EQ(result.remaining_bytes(),
Ewout van Bekkumbd338122020-09-22 16:32:45 -0700126 data.size() - varint::kMaxVarint64SizeBytes);
Wyatt Hepler80c6ee52020-01-03 09:54:58 -0800127 EXPECT_EQ(result.decoding_errors(), 2u);
128}
129
130TEST(TokenizedStringDecode, MissingArguments_IsDecodeError) {
131 auto result = kTwoArgs.Format("");
132 EXPECT_EQ(result.value(), "The %d %s");
133 EXPECT_EQ(result.value_with_errors(),
134 "The " ERR("%d MISSING") " " ERR("%s SKIPPED"));
135 EXPECT_EQ(result.remaining_bytes(), 0u);
136 EXPECT_EQ(result.decoding_errors(), 2u);
137}
138
139TEST(VarintDecode, VarintDecodeTestCases) {
140 const auto& test_data = test::varint_decoding::kTestData;
141 static_assert(sizeof(test_data) / sizeof(*test_data) > 100u);
142
143 size_t skipped = 0;
144
145 for (const auto& [d_fmt, d_expected, u_fmt, u_expected, data] : test_data) {
146 if (FormatIsSupported(d_fmt)) {
147 ASSERT_EQ(FormatString(d_fmt).Format(data).value(), d_expected);
148 ASSERT_EQ(FormatString(u_fmt).Format(data).value(), u_expected);
149 } else {
150 skipped += 1;
151 }
152
153 if (sizeof(void*) == 8) { // 64-bit systems should have full snprintf.
154 ASSERT_EQ(skipped, 0u);
155 }
156 }
157}
158
159class DecodedFormatStringTest : public ::testing::Test {
160 protected:
161 DecodedFormatStringTest()
162 : no_args_("Give 110%% sometimes"),
163 one_arg_("so you can give %d%%"),
164 two_args_("%d.%d%% of the time") {}
165
166 const FormatString no_args_;
167 const FormatString one_arg_;
168 const FormatString two_args_;
169};
170
171TEST_F(DecodedFormatStringTest, Value) {
172 EXPECT_EQ("Give 110% sometimes", no_args_.Format("").value());
173 EXPECT_EQ("so you can give 0%", one_arg_.Format("\0"sv).value());
174 EXPECT_EQ("90.9% of the time", two_args_.Format("\xB4\x01\x12").value());
175}
176
177TEST_F(DecodedFormatStringTest, FormatSuccessfully_IsOk) {
178 EXPECT_TRUE(no_args_.Format("").ok());
179 EXPECT_TRUE(one_arg_.Format("\0"sv).ok());
180 EXPECT_TRUE(two_args_.Format("\xB4\x01\x12").ok());
181}
182
183TEST_F(DecodedFormatStringTest, FormatWithDecodingErrors_IsNotOkay) {
184 EXPECT_FALSE(one_arg_.Format("").ok());
185 EXPECT_FALSE(two_args_.Format("\x80").ok());
186}
187
188TEST_F(DecodedFormatStringTest, FormatWithRemainingBytes_IsNotOkay) {
189 EXPECT_FALSE(no_args_.Format("Hello").ok());
190 EXPECT_FALSE(one_arg_.Format("\0\0"sv).ok());
191 EXPECT_FALSE(two_args_.Format("\xB4\x01\x12?").ok());
192}
193
194TEST_F(DecodedFormatStringTest, FormatWithRemainingBytesAndError_IsNotOkay) {
195 EXPECT_FALSE(
196 one_arg_.Format("\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x00"sv).ok());
197}
198
199TEST_F(DecodedFormatStringTest, RemainingBytes_NoData_IsZero) {
200 EXPECT_EQ(0u, no_args_.Format("").remaining_bytes());
201 EXPECT_EQ(0u, one_arg_.Format("").remaining_bytes());
202 EXPECT_EQ(0u, two_args_.Format("").remaining_bytes());
203}
204
205TEST_F(DecodedFormatStringTest, RemainingBytes_WithData_MatchesNumberOfArgs) {
206 EXPECT_EQ(2u, no_args_.Format("\x02\x02").remaining_bytes());
207 EXPECT_EQ(1u, one_arg_.Format("\x02\x02").remaining_bytes());
208 EXPECT_EQ(0u, two_args_.Format("\x02\x02").remaining_bytes());
209}
210
211TEST_F(DecodedFormatStringTest, ArgumentCount_NoData_MatchesNumberOfArgs) {
212 EXPECT_EQ(0u, no_args_.Format("").argument_count());
213 EXPECT_EQ(1u, one_arg_.Format("").argument_count());
214 EXPECT_EQ(2u, two_args_.Format("").argument_count());
215}
216
217TEST_F(DecodedFormatStringTest, ArgumentCount_WithData_MatchesNumberOfArgs) {
218 EXPECT_EQ(0u, no_args_.Format("\x02\x02").argument_count());
219 EXPECT_EQ(1u, one_arg_.Format("\x02\x02").argument_count());
220 EXPECT_EQ(2u, two_args_.Format("\x02\x02").argument_count());
221}
222
223TEST_F(DecodedFormatStringTest, DecodingErrors_NoData_MatchesArgumentCount) {
224 EXPECT_EQ(0u, no_args_.Format("").decoding_errors());
225 EXPECT_EQ(1u, one_arg_.Format("").decoding_errors());
226 EXPECT_EQ(2u, two_args_.Format("").decoding_errors());
227}
228
229TEST_F(DecodedFormatStringTest, DecodingErrors_OneArg_AllRemainingAreErrors) {
230 EXPECT_EQ(0u, no_args_.Format("\x02").decoding_errors());
231 EXPECT_EQ(0u, one_arg_.Format("\x02").decoding_errors());
232 EXPECT_EQ(1u, two_args_.Format("\x02").decoding_errors());
233}
234
235TEST_F(DecodedFormatStringTest, DecodingErrors_TwoArgs_IsZero) {
236 EXPECT_EQ(0u, no_args_.Format("\x02\x02").decoding_errors());
237 EXPECT_EQ(0u, one_arg_.Format("\x02\x02").decoding_errors());
238 EXPECT_EQ(0u, two_args_.Format("\x02\x02").decoding_errors());
239}
240
241} // namespace
242} // namespace pw::tokenizer