allow command to accept "--" separator
[folly.git] / folly / experimental / test / EnvUtilTest.cpp
1 /*
2  * Copyright 2017-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/EnvUtil.h>
18
19 #include <boost/algorithm/string.hpp>
20 #include <folly/Memory.h>
21 #include <folly/Subprocess.h>
22 #include <folly/portability/Fcntl.h>
23 #include <folly/portability/GTest.h>
24 #include <folly/portability/Stdlib.h>
25 #include <glog/logging.h>
26 #include <spawn.h>
27 #include <system_error>
28
29 using namespace folly;
30 using folly::test::EnvVarSaver;
31 using folly::experimental::EnvironmentState;
32 using folly::experimental::MalformedEnvironment;
33
34 DEFINE_string(
35     env_util_subprocess_binary,
36     "./env_util_subprocess",
37     "Location of the `env_util_subprocess` test helper program");
38
39 TEST(EnvVarSaverTest, ExampleNew) {
40   auto key = "hahahahaha";
41   EXPECT_EQ(nullptr, getenv(key));
42
43   PCHECK(0 == setenv(key, "", true));
44   EXPECT_STREQ("", getenv(key));
45   PCHECK(0 == unsetenv(key));
46   EXPECT_EQ(nullptr, getenv(key));
47
48   auto saver = std::make_unique<EnvVarSaver>();
49   PCHECK(0 == setenv(key, "blah", true));
50   EXPECT_STREQ("blah", getenv(key));
51   saver = nullptr;
52   EXPECT_EQ(nullptr, getenv(key));
53 }
54
55 TEST(EnvVarSaverTest, ExampleExisting) {
56   auto key = "PATH";
57   EXPECT_NE(nullptr, getenv(key));
58   auto value = std::string{getenv(key)};
59
60   auto saver = std::make_unique<EnvVarSaver>();
61   PCHECK(0 == setenv(key, "blah", true));
62   EXPECT_STREQ("blah", getenv(key));
63   saver = nullptr;
64   EXPECT_EQ(value, getenv(key));
65 }
66
67 TEST(EnvVarSaverTest, Movable) {
68   Optional<EnvVarSaver> pSaver1;
69   pSaver1.emplace();
70   auto key = "PATH";
71   EXPECT_NE(nullptr, getenv(key));
72   auto value = std::string{getenv(key)};
73   Optional<EnvVarSaver> pSaver2;
74   pSaver2.emplace(std::move(*pSaver1));
75   pSaver1.clear();
76   PCHECK(0 == setenv(key, "blah", true));
77   EXPECT_STREQ("blah", getenv(key));
78   pSaver2.clear();
79   EXPECT_EQ(value, getenv(key));
80 }
81
82 TEST(EnvironmentStateTest, FailOnEmptyString) {
83   EnvVarSaver saver{};
84   char test[4] = "A=B";
85   PCHECK(0 == putenv(test));
86   auto okState = EnvironmentState::fromCurrentEnvironment();
87   test[0] = 0;
88   EXPECT_THROW(
89       EnvironmentState::fromCurrentEnvironment(), MalformedEnvironment);
90 }
91
92 TEST(EnvironmentStateTest, MovableAndCopyable) {
93   auto initialState = EnvironmentState::fromCurrentEnvironment();
94   auto copiedState1 = EnvironmentState::empty();
95   copiedState1.operator=(initialState);
96   EnvironmentState copiedState2{initialState};
97   EXPECT_EQ(*initialState, *copiedState1);
98   EXPECT_EQ(*initialState, *copiedState2);
99   (*initialState)["foo"] = "bar";
100   EXPECT_EQ(0, copiedState1->count("foo"));
101   EXPECT_EQ(0, copiedState2->count("foo"));
102   auto movedState1 = EnvironmentState::empty();
103   movedState1.operator=(std::move(copiedState1));
104   EnvironmentState movedState2{std::move(copiedState2)};
105   EXPECT_EQ(0, movedState1->count("foo"));
106   EXPECT_EQ(0, movedState2->count("foo"));
107   initialState->erase("foo");
108   EXPECT_EQ(*initialState, *movedState1);
109   EXPECT_EQ(*initialState, *movedState2);
110 }
111
112 TEST(EnvironmentStateTest, FailOnDuplicate) {
113   EnvVarSaver saver{};
114   char test[7] = "PATG=B";
115   PCHECK(0 == putenv(test));
116   auto okState = EnvironmentState::fromCurrentEnvironment();
117   test[3] = 'H';
118   EXPECT_THROW(
119       EnvironmentState::fromCurrentEnvironment(), MalformedEnvironment);
120 }
121
122 TEST(EnvironmentStateTest, Separation) {
123   EnvVarSaver saver{};
124   auto initialState = EnvironmentState::fromCurrentEnvironment();
125   PCHECK(0 == setenv("spork", "foon", true));
126   auto updatedState = EnvironmentState::fromCurrentEnvironment();
127   EXPECT_EQ(0, initialState->count("spork"));
128   EXPECT_EQ(1, updatedState->count("spork"));
129   EXPECT_EQ("foon", (*updatedState)["spork"]);
130   updatedState->erase("spork");
131   EXPECT_EQ(0, updatedState->count("spork"));
132   EXPECT_STREQ("foon", getenv("spork"));
133 }
134
135 TEST(EnvironmentStateTest, Update) {
136   EnvVarSaver saver{};
137   auto env = EnvironmentState::fromCurrentEnvironment();
138   EXPECT_EQ(nullptr, getenv("spork"));
139   (*env)["spork"] = "foon";
140   EXPECT_EQ(nullptr, getenv("spork"));
141   env.setAsCurrentEnvironment();
142   EXPECT_STREQ("foon", getenv("spork"));
143 }
144
145 TEST(EnvironmentStateTest, forSubprocess) {
146   auto env = EnvironmentState::empty();
147   (*env)["spork"] = "foon";
148   std::vector<std::string> expected = {"spork=foon"};
149   auto vec = env.toVector();
150   EXPECT_EQ(expected, vec);
151   Subprocess subProcess{{fLS::FLAGS_env_util_subprocess_binary},
152                         {},
153                         fLS::FLAGS_env_util_subprocess_binary.c_str(),
154                         &vec};
155   EXPECT_EQ(0, subProcess.wait().exitStatus());
156 }
157
158 TEST(EnvironmentStateTest, forC) {
159   auto env = EnvironmentState::empty();
160   (*env)["spork"] = "foon";
161   EXPECT_STREQ("spork=foon", env.toPointerArray().get()[0]);
162   EXPECT_EQ(nullptr, env.toPointerArray().get()[1]);
163   char const* program = fLS::FLAGS_env_util_subprocess_binary.c_str();
164   pid_t pid;
165   PCHECK(
166       0 == posix_spawn(
167                &pid,
168                program,
169                nullptr,
170                nullptr,
171                nullptr,
172                env.toPointerArray().get()));
173   int result;
174   PCHECK(pid == waitpid(pid, &result, 0));
175   EXPECT_EQ(0, result);
176 }
177
178 TEST(EnvVarSaverTest, ExampleDeleting) {
179   auto key = "PATH";
180   EXPECT_NE(nullptr, getenv(key));
181   auto value = std::string{getenv(key)};
182
183   auto saver = std::make_unique<EnvVarSaver>();
184   PCHECK(0 == unsetenv(key));
185   EXPECT_EQ(nullptr, getenv(key));
186   saver = nullptr;
187   EXPECT_TRUE(value == getenv(key));
188 }