1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <folly/experimental/ProgramOptions.h>
18 
19 #include <folly/FileUtil.h>
20 #include <folly/Subprocess.h>
21 #include <folly/experimental/io/FsUtil.h>
22 #include <folly/portability/GTest.h>
23 
24 #include <glog/logging.h>
25 
26 namespace folly {
27 namespace test {
28 
29 namespace {
30 
getHelperPath()31 std::string getHelperPath() {
32   const auto basename = "program_options_test_helper";
33   auto path = fs::executable_path();
34   path.remove_filename() /= basename;
35   if (!fs::exists(path)) {
36     path = path.parent_path().parent_path() / basename / basename;
37   }
38   return path.string();
39 }
40 
callHelper(ProgramOptionsStyle style,std::initializer_list<std::string> args)41 std::string callHelper(
42     ProgramOptionsStyle style, std::initializer_list<std::string> args) {
43   static std::string helperPath = getHelperPath();
44 
45   std::vector<std::string> allArgs;
46   allArgs.reserve(args.size() + 1);
47   allArgs.push_back(helperPath);
48   allArgs.insert(allArgs.end(), args.begin(), args.end());
49 
50   std::vector<std::string> env;
51   switch (style) {
52     case ProgramOptionsStyle::GNU:
53       env.push_back("PROGRAM_OPTIONS_TEST_STYLE=GNU");
54       break;
55     case ProgramOptionsStyle::GFLAGS:
56       env.push_back("PROGRAM_OPTIONS_TEST_STYLE=GFLAGS");
57       break;
58   }
59 
60   Subprocess proc(allArgs, Subprocess::Options().pipeStdout(), nullptr, &env);
61   auto p = proc.communicate();
62   EXPECT_EQ(0, proc.wait().exitStatus());
63 
64   return p.first;
65 }
66 
67 } // namespace
68 
69 // name value
70 
TEST(ProgramOptionsTest,GFlagsStyleDefaultValues)71 TEST(ProgramOptionsTest, GFlagsStyleDefaultValues) {
72   EXPECT_EQ(
73       "flag_bool_true 1\n"
74       "flag_bool_false 0\n"
75       "flag_int 42\n"
76       "flag_string foo\n",
77       callHelper(ProgramOptionsStyle::GFLAGS, {}));
78 }
79 
TEST(ProgramOptionsTest,GFlagsStyleFlagsSet)80 TEST(ProgramOptionsTest, GFlagsStyleFlagsSet) {
81   EXPECT_EQ(
82       "flag_bool_true 1\n"
83       "flag_bool_false 1\n"
84       "flag_int 43\n"
85       "flag_string bar\n",
86       callHelper(
87           ProgramOptionsStyle::GFLAGS,
88           {
89               "--flag_bool_true",
90               "--flag_bool_false",
91               "--flag_int",
92               "43",
93               "--flag_string",
94               "bar",
95           }));
96 }
97 
TEST(ProgramOptionsTest,GFlagsStyleBoolFlagsNegation)98 TEST(ProgramOptionsTest, GFlagsStyleBoolFlagsNegation) {
99   EXPECT_EQ(
100       "flag_bool_true 0\n"
101       "flag_bool_false 0\n"
102       "flag_int 42\n"
103       "flag_string foo\n",
104       callHelper(
105           ProgramOptionsStyle::GFLAGS,
106           {
107               "--noflag_bool_true",
108               "--noflag_bool_false",
109           }));
110 }
111 
TEST(ProgramOptionsTest,GNUStyleDefaultValues)112 TEST(ProgramOptionsTest, GNUStyleDefaultValues) {
113   EXPECT_EQ(
114       "flag-bool-true 1\n"
115       "flag-bool-false 0\n"
116       "flag-int 42\n"
117       "flag-string foo\n",
118       callHelper(ProgramOptionsStyle::GNU, {}));
119 }
120 
TEST(ProgramOptionsTest,GNUStyleFlagsSet)121 TEST(ProgramOptionsTest, GNUStyleFlagsSet) {
122   EXPECT_EQ(
123       "flag-bool-true 1\n"
124       "flag-bool-false 1\n"
125       "flag-int 43\n"
126       "flag-string bar\n",
127       callHelper(
128           ProgramOptionsStyle::GNU,
129           {
130               "--flag-bool-true",
131               "--flag-bool-false",
132               "--flag-int",
133               "43",
134               "--flag-string",
135               "bar",
136           }));
137 }
138 
TEST(ProgramOptionsTest,GNUStyleBoolFlagsNegation)139 TEST(ProgramOptionsTest, GNUStyleBoolFlagsNegation) {
140   EXPECT_EQ(
141       "flag-bool-true 0\n"
142       "flag-bool-false 0\n"
143       "flag-int 42\n"
144       "flag-string foo\n",
145       callHelper(
146           ProgramOptionsStyle::GNU,
147           {
148               "--no-flag-bool-true",
149               "--no-flag-bool-false",
150           }));
151 }
152 
TEST(ProgramOptionsTest,GNUStyleSubCommand)153 TEST(ProgramOptionsTest, GNUStyleSubCommand) {
154   EXPECT_EQ(
155       "flag-bool-true 1\n"
156       "flag-bool-false 1\n"
157       "flag-int 43\n"
158       "flag-string foo\n"
159       "command hello\n"
160       "arg --wtf\n"
161       "arg 100\n"
162       "arg -x\n"
163       "arg -xy\n",
164       callHelper(
165           ProgramOptionsStyle::GNU,
166           {
167               "--flag-bool-false",
168               "hello",
169               "--wtf",
170               "--flag-int",
171               "43",
172               "100",
173               "-x",
174               "-xy",
175           }));
176 }
177 
TEST(ProgramOptionsTest,GNUStyleSubCommandUnrecognizedOptionFirst)178 TEST(ProgramOptionsTest, GNUStyleSubCommandUnrecognizedOptionFirst) {
179   EXPECT_EQ(
180       "flag-bool-true 1\n"
181       "flag-bool-false 1\n"
182       "flag-int 43\n"
183       "flag-string foo\n"
184       "arg --wtf\n"
185       "arg hello\n"
186       "arg 100\n"
187       "arg -x\n"
188       "arg -xy\n",
189       callHelper(
190           ProgramOptionsStyle::GNU,
191           {
192               "--flag-bool-false",
193               "--wtf",
194               "hello",
195               "--flag-int",
196               "43",
197               "100",
198               "-x",
199               "-xy",
200           }));
201 }
202 
203 } // namespace test
204 } // namespace folly
205