/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* 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
*
* http://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 <folly/cli/ProgramOptions.h>
#include <folly/FileUtil.h>
#include <folly/Subprocess.h>
#include <folly/experimental/io/FsUtil.h>
#include <folly/portability/GTest.h>
#include <cstdlib>
#include <glog/logging.h>
namespace folly {
namespace test {
namespace {
std::string getHelperPath() {
const auto* envPath = getenv("FOLLY_PROGRAM_OPTIONS_TEST_HELPER");
if (envPath) {
return envPath;
}
const auto basename = "program_options_test_helper";
auto path = fs::executable_path();
path.remove_filename() /= basename;
if (!fs::exists(path)) {
path = path.parent_path().parent_path() / basename / basename;
}
return path.string();
}
std::string callHelper(
ProgramOptionsStyle style, std::initializer_list<std::string> args) {
static std::string helperPath = getHelperPath();
std::vector<std::string> allArgs;
allArgs.reserve(args.size() + 1);
allArgs.push_back(helperPath);
allArgs.insert(allArgs.end(), args.begin(), args.end());
std::vector<std::string> env;
switch (style) {
case ProgramOptionsStyle::GNU:
env.push_back("PROGRAM_OPTIONS_TEST_STYLE=GNU");
break;
case ProgramOptionsStyle::GFLAGS:
env.push_back("PROGRAM_OPTIONS_TEST_STYLE=GFLAGS");
break;
}
Subprocess proc(allArgs, Subprocess::Options().pipeStdout(), nullptr, &env);
auto p = proc.communicate();
EXPECT_EQ(0, proc.wait().exitStatus());
return p.first;
}
} // namespace
// name value
TEST(ProgramOptionsTest, GFlagsStyleDefaultValues) {
EXPECT_EQ(
"flag_bool_true 1\n"
"flag_bool_false 0\n"
"flag_int 42\n"
"flag_string foo\n",
callHelper(ProgramOptionsStyle::GFLAGS, {}));
}
TEST(ProgramOptionsTest, GFlagsStyleFlagsSet) {
EXPECT_EQ(
"flag_bool_true 1\n"
"flag_bool_false 1\n"
"flag_int 43\n"
"flag_string bar\n",
callHelper(
ProgramOptionsStyle::GFLAGS,
{
"--flag_bool_true",
"--flag_bool_false",
"--flag_int",
"43",
"--flag_string",
"bar",
}));
}
TEST(ProgramOptionsTest, GFlagsStyleBoolFlagsNegation) {
EXPECT_EQ(
"flag_bool_true 0\n"
"flag_bool_false 0\n"
"flag_int 42\n"
"flag_string foo\n",
callHelper(
ProgramOptionsStyle::GFLAGS,
{
"--noflag_bool_true",
"--noflag_bool_false",
}));
}
TEST(ProgramOptionsTest, GNUStyleDefaultValues) {
EXPECT_EQ(
"flag-bool-true 1\n"
"flag-bool-false 0\n"
"flag-int 42\n"
"flag-string foo\n",
callHelper(ProgramOptionsStyle::GNU, {}));
}
TEST(ProgramOptionsTest, GNUStyleFlagsSet) {
EXPECT_EQ(
"flag-bool-true 1\n"
"flag-bool-false 1\n"
"flag-int 43\n"
"flag-string bar\n",
callHelper(
ProgramOptionsStyle::GNU,
{
"--flag-bool-true",
"--flag-bool-false",
"--flag-int",
"43",
"--flag-string",
"bar",
}));
}
TEST(ProgramOptionsTest, GNUStyleBoolFlagsNegation) {
EXPECT_EQ(
"flag-bool-true 0\n"
"flag-bool-false 0\n"
"flag-int 42\n"
"flag-string foo\n",
callHelper(
ProgramOptionsStyle::GNU,
{
"--no-flag-bool-true",
"--no-flag-bool-false",
}));
}
TEST(ProgramOptionsTest, GNUStyleSubCommand) {
EXPECT_EQ(
"flag-bool-true 1\n"
"flag-bool-false 1\n"
"flag-int 43\n"
"flag-string foo\n"
"command hello\n"
"arg --wtf\n"
"arg 100\n"
"arg -x\n"
"arg -xy\n",
callHelper(
ProgramOptionsStyle::GNU,
{
"--flag-bool-false",
"hello",
"--wtf",
"--flag-int",
"43",
"100",
"-x",
"-xy",
}));
}
TEST(ProgramOptionsTest, GNUStyleSubCommandUnrecognizedOptionFirst) {
EXPECT_EQ(
"flag-bool-true 1\n"
"flag-bool-false 1\n"
"flag-int 43\n"
"flag-string foo\n"
"arg --wtf\n"
"arg hello\n"
"arg 100\n"
"arg -x\n"
"arg -xy\n",
callHelper(
ProgramOptionsStyle::GNU,
{
"--flag-bool-false",
"--wtf",
"hello",
"--flag-int",
"43",
"100",
"-x",
"-xy",
}));
}
} // namespace test
} // namespace folly