1 // Copyright 2023 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_rpc/fuzz/argparse.h"
16
17 #include <cstdint>
18 #include <limits>
19
20 #include "pw_string/string_builder.h"
21 #include "pw_unit_test/framework.h"
22
23 namespace pw::rpc::fuzz {
24 namespace {
25
TEST(ArgsParseTest,ParseBoolFlag)26 TEST(ArgsParseTest, ParseBoolFlag) {
27 auto parser1 = BoolParser("-t", "--true").set_default(true);
28 auto parser2 = BoolParser("-f").set_default(false);
29 EXPECT_TRUE(parser1.value());
30 EXPECT_FALSE(parser2.value());
31
32 EXPECT_EQ(parser1.Parse("-t"), ParseStatus::kParsedOne);
33 EXPECT_EQ(parser2.Parse("-t"), ParseStatus::kParseMismatch);
34 EXPECT_TRUE(parser1.value());
35 EXPECT_FALSE(parser2.value());
36
37 EXPECT_EQ(parser1.Parse("--true"), ParseStatus::kParsedOne);
38 EXPECT_EQ(parser2.Parse("--true"), ParseStatus::kParseMismatch);
39 EXPECT_TRUE(parser1.value());
40 EXPECT_FALSE(parser2.value());
41
42 EXPECT_EQ(parser1.Parse("--no-true"), ParseStatus::kParsedOne);
43 EXPECT_EQ(parser2.Parse("--no-true"), ParseStatus::kParseMismatch);
44 EXPECT_FALSE(parser1.value());
45 EXPECT_FALSE(parser2.value());
46
47 EXPECT_EQ(parser1.Parse("-f"), ParseStatus::kParseMismatch);
48 EXPECT_EQ(parser2.Parse("-f"), ParseStatus::kParsedOne);
49 EXPECT_FALSE(parser1.value());
50 EXPECT_TRUE(parser2.value());
51 }
52
53 template <typename T>
ParseUnsignedFlag()54 void ParseUnsignedFlag() {
55 auto parser = UnsignedParser<T>("-u", "--unsigned").set_default(137);
56 EXPECT_EQ(parser.value(), 137u);
57
58 // Wrong name.
59 EXPECT_EQ(parser.Parse("-s"), ParseStatus::kParseMismatch);
60 EXPECT_EQ(parser.Parse("--signed"), ParseStatus::kParseMismatch);
61 EXPECT_EQ(parser.value(), 137u);
62
63 // Missing values.
64 EXPECT_EQ(parser.Parse("-u"), ParseStatus::kParseFailure);
65 EXPECT_EQ(parser.Parse("--unsigned"), ParseStatus::kParseFailure);
66 EXPECT_EQ(parser.value(), 137u);
67
68 // Non-numeric values.
69 EXPECT_EQ(parser.Parse("-u", "foo"), ParseStatus::kParseFailure);
70 EXPECT_EQ(parser.Parse("--unsigned", "bar"), ParseStatus::kParseFailure);
71 EXPECT_EQ(parser.value(), 137u);
72
73 // Minimum values.
74 EXPECT_EQ(parser.Parse("-u", "0"), ParseStatus::kParsedTwo);
75 EXPECT_EQ(parser.Parse("--unsigned", "0"), ParseStatus::kParsedTwo);
76 EXPECT_EQ(parser.value(), 0u);
77
78 // Maximum values.
79 T max = std::numeric_limits<T>::max();
80 StringBuffer<32> buf;
81 buf << max;
82 EXPECT_EQ(parser.Parse("-u", buf.c_str()), ParseStatus::kParsedTwo);
83 EXPECT_EQ(parser.value(), max);
84 EXPECT_EQ(parser.Parse("--unsigned", buf.c_str()), ParseStatus::kParsedTwo);
85 EXPECT_EQ(parser.value(), max);
86
87 // Out of-range value.
88 if (max < std::numeric_limits<uint64_t>::max()) {
89 buf.clear();
90 buf << (max + 1ULL);
91 EXPECT_EQ(parser.Parse("-u", buf.c_str()), ParseStatus::kParseFailure);
92 EXPECT_EQ(parser.Parse("--unsigned", buf.c_str()),
93 ParseStatus::kParseFailure);
94 EXPECT_EQ(parser.value(), max);
95 }
96 }
97
TEST(ArgsParseTest,ParseUnsignedFlags)98 TEST(ArgsParseTest, ParseUnsignedFlags) {
99 ParseUnsignedFlag<uint8_t>();
100 ParseUnsignedFlag<uint16_t>();
101 ParseUnsignedFlag<uint32_t>();
102 ParseUnsignedFlag<uint64_t>();
103 }
104
TEST(ArgsParseTest,ParsePositional)105 TEST(ArgsParseTest, ParsePositional) {
106 auto parser = UnsignedParser<size_t>("positional").set_default(1);
107 EXPECT_EQ(parser.Parse("-p", "2"), ParseStatus::kParseFailure);
108 EXPECT_EQ(parser.value(), 1u);
109
110 EXPECT_EQ(parser.Parse("--positional", "2"), ParseStatus::kParseFailure);
111 EXPECT_EQ(parser.value(), 1u);
112
113 // Second arg is ignored..
114 EXPECT_EQ(parser.Parse("2", "3"), ParseStatus::kParsedOne);
115 EXPECT_EQ(parser.value(), 2u);
116
117 // Positional only matches once.
118 EXPECT_EQ(parser.Parse("3"), ParseStatus::kParseMismatch);
119 EXPECT_EQ(parser.value(), 2u);
120 }
121
TEST(ArgsParseTest,PrintUsage)122 TEST(ArgsParseTest, PrintUsage) {
123 // Just verify it compiles and runs.
124 Vector<ArgParserVariant, 3> parsers = {
125 BoolParser("-v", "--verbose").set_default(false),
126 UnsignedParser<size_t>("-r", "--runs").set_default(1000),
127 UnsignedParser<size_t>("port").set_default(11111),
128 };
129 PrintUsage(parsers, "test-bin");
130 }
131
CheckArgs(Vector<ArgParserVariant> & parsers,bool verbose,size_t runs,uint16_t port)132 void CheckArgs(Vector<ArgParserVariant>& parsers,
133 bool verbose,
134 size_t runs,
135 uint16_t port) {
136 bool actual_verbose = false;
137 EXPECT_EQ(GetArg(parsers, "--verbose", &actual_verbose), OkStatus());
138 EXPECT_EQ(verbose, actual_verbose);
139 EXPECT_EQ(ResetArg(parsers, "--verbose"), OkStatus());
140
141 size_t actual_runs = 0u;
142 EXPECT_EQ(GetArg(parsers, "--runs", &actual_runs), OkStatus());
143 EXPECT_EQ(runs, actual_runs);
144 EXPECT_EQ(ResetArg(parsers, "--runs"), OkStatus());
145
146 uint16_t actual_port = 0u;
147 EXPECT_EQ(GetArg(parsers, "port", &actual_port), OkStatus());
148 EXPECT_EQ(port, actual_port);
149 EXPECT_EQ(ResetArg(parsers, "port"), OkStatus());
150 }
151
TEST(ArgsParseTest,ParseArgs)152 TEST(ArgsParseTest, ParseArgs) {
153 Vector<ArgParserVariant, 3> parsers{
154 BoolParser("-v", "--verbose").set_default(false),
155 UnsignedParser<size_t>("-r", "--runs").set_default(1000),
156 UnsignedParser<uint16_t>("port").set_default(11111),
157 };
158
159 char const* argv1[] = {"test-bin"};
160 EXPECT_EQ(ParseArgs(parsers, 1, const_cast<char**>(argv1)), OkStatus());
161 CheckArgs(parsers, false, 1000, 11111);
162
163 char const* argv2[] = {"test-bin", "22222"};
164 EXPECT_EQ(ParseArgs(parsers, 2, const_cast<char**>(argv2)), OkStatus());
165 CheckArgs(parsers, false, 1000, 22222);
166
167 // Out of range argument.
168 char const* argv3[] = {"test-bin", "65536"};
169 EXPECT_EQ(ParseArgs(parsers, 2, const_cast<char**>(argv3)),
170 Status::InvalidArgument());
171
172 // Extra argument.
173 char const* argv4[] = {"test-bin", "1", "2"};
174 EXPECT_EQ(ParseArgs(parsers, 3, const_cast<char**>(argv4)),
175 Status::InvalidArgument());
176 EXPECT_EQ(ResetArg(parsers, "port"), OkStatus());
177
178 // Flag missing value.
179 char const* argv5[] = {"test-bin", "--runs"};
180 EXPECT_EQ(ParseArgs(parsers, 2, const_cast<char**>(argv5)),
181 Status::InvalidArgument());
182
183 char const* argv6[] = {"test-bin", "-v", "33333", "--runs", "300"};
184 EXPECT_EQ(ParseArgs(parsers, 5, const_cast<char**>(argv6)), OkStatus());
185 CheckArgs(parsers, true, 300, 33333);
186
187 char const* argv7[] = {"test-bin", "-r", "400", "--verbose"};
188 EXPECT_EQ(ParseArgs(parsers, 4, const_cast<char**>(argv7)), OkStatus());
189 CheckArgs(parsers, true, 400, 11111);
190
191 char const* argv8[] = {"test-bin", "--no-verbose", "-r", "5000", "55555"};
192 EXPECT_EQ(ParseArgs(parsers, 5, const_cast<char**>(argv8)), OkStatus());
193 CheckArgs(parsers, false, 5000, 55555);
194 }
195
196 } // namespace
197 } // namespace pw::rpc::fuzz
198