allow command to accept "--" separator
[folly.git] / folly / experimental / test / NestedCommandLineAppTest.cpp
1 /*
2  * Copyright 2015-present Facebook, Inc.
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/NestedCommandLineApp.h>
18
19 #include <folly/Subprocess.h>
20 #include <folly/experimental/io/FsUtil.h>
21 #include <folly/portability/GTest.h>
22 #include <glog/logging.h>
23
24 namespace folly { namespace test {
25
26 namespace {
27
28 std::string getHelperPath() {
29   const auto basename = "nested_command_line_app_test_helper";
30   auto path = fs::executable_path();
31   path.remove_filename() /= basename;
32   if (!fs::exists(path)) {
33     path = path.parent_path().parent_path() / basename / basename;
34   }
35   return path.string();
36 }
37
38 std::string callHelper(std::initializer_list<std::string> args,
39                        int expectedExitCode = 0,
40                        int stdoutFd = -1) {
41   static std::string helperPath = getHelperPath();
42
43   std::vector<std::string> allArgs;
44   allArgs.reserve(args.size() + 1);
45   allArgs.push_back(helperPath);
46   allArgs.insert(allArgs.end(), args.begin(), args.end());
47
48   Subprocess::Options options;
49   if (stdoutFd != -1) {
50     options.stdoutFd(stdoutFd);
51   } else {
52     options.pipeStdout();
53   }
54   options.pipeStderr();
55
56   Subprocess proc(allArgs, options);
57   auto p = proc.communicate();
58   EXPECT_EQ(expectedExitCode, proc.wait().exitStatus());
59
60   return p.first;
61 }
62
63 } // namespace
64
65 TEST(ProgramOptionsTest, Errors) {
66   callHelper({}, 1);
67   callHelper({"--wtf", "foo"}, 1);
68   callHelper({"qux"}, 1);
69   callHelper({"--global-foo", "x", "foo"}, 1);
70 }
71
72 TEST(ProgramOptionsTest, Help) {
73   // Not actually checking help output, just verifying that help doesn't fail
74   callHelper({"--version"});
75   callHelper({"--help"});
76   callHelper({"--help", "foo"});
77   callHelper({"--help", "bar"});
78   callHelper({"--help", "--", "bar"});
79   callHelper({"help"});
80   callHelper({"help", "foo"});
81   callHelper({"help", "bar"});
82
83   // wrong command name
84   callHelper({"--help", "qux"}, 1);
85   callHelper({"help", "qux"}, 1);
86
87   // anything after -- is parsed as arguments
88   callHelper({"--", "help", "bar"}, 1);
89 }
90
91 TEST(ProgramOptionsTest, DevFull) {
92   folly::File full("/dev/full", O_RDWR);
93   callHelper({"--help"}, 1, full.fd());
94 }
95
96 TEST(ProgramOptionsTest, CutArguments) {
97   // anything after -- is parsed as arguments
98   EXPECT_EQ(
99       "running foo\n"
100       "foo global-foo 43\n"
101       "foo local-foo 42\n"
102       "foo arg b\n"
103       "foo arg --local-foo\n"
104       "foo arg 44\n"
105       "foo arg a\n",
106       callHelper(
107           {"foo", "--global-foo", "43", "--", "b", "--local-foo", "44", "a"}));
108 }
109
110 TEST(ProgramOptionsTest, Success) {
111   EXPECT_EQ(
112       "running foo\n"
113       "foo global-foo 42\n"
114       "foo local-foo 42\n",
115       callHelper({"foo"}));
116
117   EXPECT_EQ(
118       "running foo\n"
119       "foo global-foo 43\n"
120       "foo local-foo 44\n"
121       "foo arg a\n"
122       "foo arg b\n",
123       callHelper({"--global-foo", "43", "foo", "--local-foo", "44",
124                   "a", "b"}));
125
126   // Check that global flags can still be given after the command
127   EXPECT_EQ(
128       "running foo\n"
129       "foo global-foo 43\n"
130       "foo local-foo 44\n"
131       "foo arg a\n"
132       "foo arg b\n",
133       callHelper({"foo", "--global-foo", "43", "--local-foo", "44",
134                  "a", "b"}));
135 }
136
137 TEST(ProgramOptionsTest, Aliases) {
138   EXPECT_EQ(
139       "running foo\n"
140       "foo global-foo 43\n"
141       "foo local-foo 44\n"
142       "foo arg a\n"
143       "foo arg b\n",
144       callHelper({"--global-foo", "43", "bar", "--local-foo", "44",
145                   "a", "b"}));
146 }
147
148 } // namespace test
149 } // namespace folly