// 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 #include #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 void ParseUnsignedFlag() { auto parser = UnsignedParser("-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::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::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(); ParseUnsignedFlag(); ParseUnsignedFlag(); ParseUnsignedFlag(); } TEST(ArgsParseTest, ParsePositional) { auto parser = UnsignedParser("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 parsers = { BoolParser("-v", "--verbose").set_default(false), UnsignedParser("-r", "--runs").set_default(1000), UnsignedParser("port").set_default(11111), }; PrintUsage(parsers, "test-bin"); } void CheckArgs(Vector& 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 parsers{ BoolParser("-v", "--verbose").set_default(false), UnsignedParser("-r", "--runs").set_default(1000), UnsignedParser("port").set_default(11111), }; char const* argv1[] = {"test-bin"}; EXPECT_EQ(ParseArgs(parsers, 1, const_cast(argv1)), OkStatus()); CheckArgs(parsers, false, 1000, 11111); char const* argv2[] = {"test-bin", "22222"}; EXPECT_EQ(ParseArgs(parsers, 2, const_cast(argv2)), OkStatus()); CheckArgs(parsers, false, 1000, 22222); // Out of range argument. char const* argv3[] = {"test-bin", "65536"}; EXPECT_EQ(ParseArgs(parsers, 2, const_cast(argv3)), Status::InvalidArgument()); // Extra argument. char const* argv4[] = {"test-bin", "1", "2"}; EXPECT_EQ(ParseArgs(parsers, 3, const_cast(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(argv5)), Status::InvalidArgument()); char const* argv6[] = {"test-bin", "-v", "33333", "--runs", "300"}; EXPECT_EQ(ParseArgs(parsers, 5, const_cast(argv6)), OkStatus()); CheckArgs(parsers, true, 300, 33333); char const* argv7[] = {"test-bin", "-r", "400", "--verbose"}; EXPECT_EQ(ParseArgs(parsers, 4, const_cast(argv7)), OkStatus()); CheckArgs(parsers, true, 400, 11111); char const* argv8[] = {"test-bin", "--no-verbose", "-r", "5000", "55555"}; EXPECT_EQ(ParseArgs(parsers, 5, const_cast(argv8)), OkStatus()); CheckArgs(parsers, false, 5000, 55555); } } // namespace } // namespace pw::rpc::fuzz