| // Copyright 2023 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #include "pw_rpc/fuzz/argparse.h" |
| |
| #include <cstdint> |
| #include <limits> |
| |
| #include "pw_string/string_builder.h" |
| #include "pw_unit_test/framework.h" |
| |
| namespace pw::rpc::fuzz { |
| namespace { |
| |
| TEST(ArgsParseTest, ParseBoolFlag) { |
| auto parser1 = BoolParser("-t", "--true").set_default(true); |
| auto parser2 = BoolParser("-f").set_default(false); |
| EXPECT_TRUE(parser1.value()); |
| EXPECT_FALSE(parser2.value()); |
| |
| EXPECT_EQ(parser1.Parse("-t"), ParseStatus::kParsedOne); |
| EXPECT_EQ(parser2.Parse("-t"), ParseStatus::kParseMismatch); |
| EXPECT_TRUE(parser1.value()); |
| EXPECT_FALSE(parser2.value()); |
| |
| EXPECT_EQ(parser1.Parse("--true"), ParseStatus::kParsedOne); |
| EXPECT_EQ(parser2.Parse("--true"), ParseStatus::kParseMismatch); |
| EXPECT_TRUE(parser1.value()); |
| EXPECT_FALSE(parser2.value()); |
| |
| EXPECT_EQ(parser1.Parse("--no-true"), ParseStatus::kParsedOne); |
| EXPECT_EQ(parser2.Parse("--no-true"), ParseStatus::kParseMismatch); |
| EXPECT_FALSE(parser1.value()); |
| EXPECT_FALSE(parser2.value()); |
| |
| EXPECT_EQ(parser1.Parse("-f"), ParseStatus::kParseMismatch); |
| EXPECT_EQ(parser2.Parse("-f"), ParseStatus::kParsedOne); |
| EXPECT_FALSE(parser1.value()); |
| EXPECT_TRUE(parser2.value()); |
| } |
| |
| template <typename T> |
| void ParseUnsignedFlag() { |
| auto parser = UnsignedParser<T>("-u", "--unsigned").set_default(137); |
| EXPECT_EQ(parser.value(), 137u); |
| |
| // Wrong name. |
| EXPECT_EQ(parser.Parse("-s"), ParseStatus::kParseMismatch); |
| EXPECT_EQ(parser.Parse("--signed"), ParseStatus::kParseMismatch); |
| EXPECT_EQ(parser.value(), 137u); |
| |
| // Missing values. |
| EXPECT_EQ(parser.Parse("-u"), ParseStatus::kParseFailure); |
| EXPECT_EQ(parser.Parse("--unsigned"), ParseStatus::kParseFailure); |
| EXPECT_EQ(parser.value(), 137u); |
| |
| // Non-numeric values. |
| EXPECT_EQ(parser.Parse("-u", "foo"), ParseStatus::kParseFailure); |
| EXPECT_EQ(parser.Parse("--unsigned", "bar"), ParseStatus::kParseFailure); |
| EXPECT_EQ(parser.value(), 137u); |
| |
| // Minimum values. |
| EXPECT_EQ(parser.Parse("-u", "0"), ParseStatus::kParsedTwo); |
| EXPECT_EQ(parser.Parse("--unsigned", "0"), ParseStatus::kParsedTwo); |
| EXPECT_EQ(parser.value(), 0u); |
| |
| // Maximum values. |
| T max = std::numeric_limits<T>::max(); |
| StringBuffer<32> buf; |
| buf << max; |
| EXPECT_EQ(parser.Parse("-u", buf.c_str()), ParseStatus::kParsedTwo); |
| EXPECT_EQ(parser.value(), max); |
| EXPECT_EQ(parser.Parse("--unsigned", buf.c_str()), ParseStatus::kParsedTwo); |
| EXPECT_EQ(parser.value(), max); |
| |
| // Out of-range value. |
| if (max < std::numeric_limits<uint64_t>::max()) { |
| buf.clear(); |
| buf << (max + 1ULL); |
| EXPECT_EQ(parser.Parse("-u", buf.c_str()), ParseStatus::kParseFailure); |
| EXPECT_EQ(parser.Parse("--unsigned", buf.c_str()), |
| ParseStatus::kParseFailure); |
| EXPECT_EQ(parser.value(), max); |
| } |
| } |
| |
| TEST(ArgsParseTest, ParseUnsignedFlags) { |
| ParseUnsignedFlag<uint8_t>(); |
| ParseUnsignedFlag<uint16_t>(); |
| ParseUnsignedFlag<uint32_t>(); |
| ParseUnsignedFlag<uint64_t>(); |
| } |
| |
| TEST(ArgsParseTest, ParsePositional) { |
| auto parser = UnsignedParser<size_t>("positional").set_default(1); |
| EXPECT_EQ(parser.Parse("-p", "2"), ParseStatus::kParseFailure); |
| EXPECT_EQ(parser.value(), 1u); |
| |
| EXPECT_EQ(parser.Parse("--positional", "2"), ParseStatus::kParseFailure); |
| EXPECT_EQ(parser.value(), 1u); |
| |
| // Second arg is ignored.. |
| EXPECT_EQ(parser.Parse("2", "3"), ParseStatus::kParsedOne); |
| EXPECT_EQ(parser.value(), 2u); |
| |
| // Positional only matches once. |
| EXPECT_EQ(parser.Parse("3"), ParseStatus::kParseMismatch); |
| EXPECT_EQ(parser.value(), 2u); |
| } |
| |
| TEST(ArgsParseTest, PrintUsage) { |
| // Just verify it compiles and runs. |
| Vector<ArgParserVariant, 3> parsers = { |
| BoolParser("-v", "--verbose").set_default(false), |
| UnsignedParser<size_t>("-r", "--runs").set_default(1000), |
| UnsignedParser<size_t>("port").set_default(11111), |
| }; |
| PrintUsage(parsers, "test-bin"); |
| } |
| |
| void CheckArgs(Vector<ArgParserVariant>& parsers, |
| bool verbose, |
| size_t runs, |
| uint16_t port) { |
| bool actual_verbose = false; |
| EXPECT_EQ(GetArg(parsers, "--verbose", &actual_verbose), OkStatus()); |
| EXPECT_EQ(verbose, actual_verbose); |
| EXPECT_EQ(ResetArg(parsers, "--verbose"), OkStatus()); |
| |
| size_t actual_runs = 0u; |
| EXPECT_EQ(GetArg(parsers, "--runs", &actual_runs), OkStatus()); |
| EXPECT_EQ(runs, actual_runs); |
| EXPECT_EQ(ResetArg(parsers, "--runs"), OkStatus()); |
| |
| uint16_t actual_port = 0u; |
| EXPECT_EQ(GetArg(parsers, "port", &actual_port), OkStatus()); |
| EXPECT_EQ(port, actual_port); |
| EXPECT_EQ(ResetArg(parsers, "port"), OkStatus()); |
| } |
| |
| TEST(ArgsParseTest, ParseArgs) { |
| Vector<ArgParserVariant, 3> parsers{ |
| BoolParser("-v", "--verbose").set_default(false), |
| UnsignedParser<size_t>("-r", "--runs").set_default(1000), |
| UnsignedParser<uint16_t>("port").set_default(11111), |
| }; |
| |
| char const* argv1[] = {"test-bin"}; |
| EXPECT_EQ(ParseArgs(parsers, 1, const_cast<char**>(argv1)), OkStatus()); |
| CheckArgs(parsers, false, 1000, 11111); |
| |
| char const* argv2[] = {"test-bin", "22222"}; |
| EXPECT_EQ(ParseArgs(parsers, 2, const_cast<char**>(argv2)), OkStatus()); |
| CheckArgs(parsers, false, 1000, 22222); |
| |
| // Out of range argument. |
| char const* argv3[] = {"test-bin", "65536"}; |
| EXPECT_EQ(ParseArgs(parsers, 2, const_cast<char**>(argv3)), |
| Status::InvalidArgument()); |
| |
| // Extra argument. |
| char const* argv4[] = {"test-bin", "1", "2"}; |
| EXPECT_EQ(ParseArgs(parsers, 3, const_cast<char**>(argv4)), |
| Status::InvalidArgument()); |
| EXPECT_EQ(ResetArg(parsers, "port"), OkStatus()); |
| |
| // Flag missing value. |
| char const* argv5[] = {"test-bin", "--runs"}; |
| EXPECT_EQ(ParseArgs(parsers, 2, const_cast<char**>(argv5)), |
| Status::InvalidArgument()); |
| |
| char const* argv6[] = {"test-bin", "-v", "33333", "--runs", "300"}; |
| EXPECT_EQ(ParseArgs(parsers, 5, const_cast<char**>(argv6)), OkStatus()); |
| CheckArgs(parsers, true, 300, 33333); |
| |
| char const* argv7[] = {"test-bin", "-r", "400", "--verbose"}; |
| EXPECT_EQ(ParseArgs(parsers, 4, const_cast<char**>(argv7)), OkStatus()); |
| CheckArgs(parsers, true, 400, 11111); |
| |
| char const* argv8[] = {"test-bin", "--no-verbose", "-r", "5000", "55555"}; |
| EXPECT_EQ(ParseArgs(parsers, 5, const_cast<char**>(argv8)), OkStatus()); |
| CheckArgs(parsers, false, 5000, 55555); |
| } |
| |
| } // namespace |
| } // namespace pw::rpc::fuzz |