Disable EnvUtil::setAsCurrentEnvironment() on platforms without clearenv()
[folly.git] / folly / experimental / test / EnvUtilTest.cpp
1 /*
2  * Copyright 2017 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 = 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 = 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 #if __linux__ && !FOLLY_MOBILE
136 TEST(EnvironmentStateTest, Update) {
137   EnvVarSaver saver{};
138   auto env = EnvironmentState::fromCurrentEnvironment();
139   EXPECT_EQ(nullptr, getenv("spork"));
140   (*env)["spork"] = "foon";
141   EXPECT_EQ(nullptr, getenv("spork"));
142   env.setAsCurrentEnvironment();
143   EXPECT_STREQ("foon", getenv("spork"));
144 }
145 #endif
146
147 TEST(EnvironmentStateTest, forSubprocess) {
148   auto env = EnvironmentState::empty();
149   (*env)["spork"] = "foon";
150   std::vector<std::string> expected = {"spork=foon"};
151   auto vec = env.toVector();
152   EXPECT_EQ(expected, vec);
153   Subprocess subProcess{{fLS::FLAGS_env_util_subprocess_binary},
154                         {},
155                         fLS::FLAGS_env_util_subprocess_binary.c_str(),
156                         &vec};
157   EXPECT_EQ(0, subProcess.wait().exitStatus());
158 }
159
160 TEST(EnvironmentStateTest, forC) {
161   auto env = EnvironmentState::empty();
162   (*env)["spork"] = "foon";
163   EXPECT_STREQ("spork=foon", env.toPointerArray().get()[0]);
164   EXPECT_EQ(nullptr, env.toPointerArray().get()[1]);
165   char const* program = fLS::FLAGS_env_util_subprocess_binary.c_str();
166   pid_t pid;
167   PCHECK(
168       0 == posix_spawn(
169                &pid,
170                program,
171                nullptr,
172                nullptr,
173                nullptr,
174                env.toPointerArray().get()));
175   int result;
176   PCHECK(pid == waitpid(pid, &result, 0));
177   EXPECT_EQ(0, result);
178 }
179
180 TEST(EnvVarSaverTest, ExampleDeleting) {
181   auto key = "PATH";
182   EXPECT_NE(nullptr, getenv(key));
183   auto value = std::string{getenv(key)};
184
185   auto saver = make_unique<EnvVarSaver>();
186   PCHECK(0 == unsetenv(key));
187   EXPECT_EQ(nullptr, getenv(key));
188   saver = nullptr;
189   EXPECT_TRUE(value == getenv(key));
190 }