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/TestUtil.h>
18 
19 #include <system_error>
20 
21 #include <boost/algorithm/string.hpp>
22 #include <glog/logging.h>
23 
24 #include <folly/Memory.h>
25 #include <folly/portability/Fcntl.h>
26 #include <folly/portability/GTest.h>
27 #include <folly/portability/Stdlib.h>
28 
29 using namespace folly;
30 using namespace folly::test;
31 
TEST(TemporaryFile,Simple)32 TEST(TemporaryFile, Simple) {
33   int fd = -1;
34   char c = 'x';
35   {
36     TemporaryFile f;
37     EXPECT_FALSE(f.path().empty());
38     EXPECT_TRUE(f.path().is_absolute());
39     fd = f.fd();
40     EXPECT_LE(0, fd);
41     ssize_t r = write(fd, &c, 1);
42     EXPECT_EQ(1, r);
43   }
44 
45   msvcSuppressAbortOnInvalidParams([&] {
46     // The file must have been closed.  This assumes that no other thread
47     // has opened another file in the meanwhile, which is a sane assumption
48     // to make in this test.
49     ssize_t r = write(fd, &c, 1);
50     int savedErrno = errno;
51     EXPECT_EQ(-1, r);
52     EXPECT_EQ(EBADF, savedErrno);
53   });
54 }
55 
TEST(TemporaryFile,EarlyClose)56 TEST(TemporaryFile, EarlyClose) {
57   fs::path p;
58   {
59     TemporaryFile f;
60     p = f.path();
61     EXPECT_TRUE(fs::exists(p));
62     f.close();
63     EXPECT_EQ(-1, f.fd());
64     EXPECT_TRUE(fs::exists(p));
65   }
66   EXPECT_FALSE(fs::exists(p));
67 }
68 
TEST(TemporaryFile,Prefix)69 TEST(TemporaryFile, Prefix) {
70   TemporaryFile f("Foo");
71   EXPECT_TRUE(f.path().is_absolute());
72   EXPECT_TRUE(
73       boost::algorithm::starts_with(f.path().filename().native(), "Foo"));
74 }
75 
TEST(TemporaryFile,PathPrefix)76 TEST(TemporaryFile, PathPrefix) {
77   TemporaryFile f("Foo", ".");
78   EXPECT_EQ(fs::path("."), f.path().parent_path());
79   EXPECT_TRUE(
80       boost::algorithm::starts_with(f.path().filename().native(), "Foo"));
81 }
82 
TEST(TemporaryFile,NoSuchPath)83 TEST(TemporaryFile, NoSuchPath) {
84   EXPECT_THROW({ TemporaryFile f("", "/no/such/path"); }, std::system_error);
85 }
86 
TEST(TemporaryFile,moveAssignment)87 TEST(TemporaryFile, moveAssignment) {
88   TemporaryFile f;
89   int fd;
90 
91   EXPECT_TRUE(f.path().is_absolute());
92   {
93     TemporaryFile g("Foo", ".");
94     EXPECT_NE(g.fd(), -1);
95     fd = g.fd();
96     f = std::move(g);
97   }
98   EXPECT_EQ(fs::path("."), f.path().parent_path());
99   EXPECT_EQ(f.fd(), fd);
100 
101   TemporaryFile h = TemporaryFile("FooBar", ".");
102   EXPECT_NE(h.fd(), -1);
103 }
104 
TEST(TemporaryFile,moveCtor)105 TEST(TemporaryFile, moveCtor) {
106   struct FooBar {
107     TemporaryFile f_;
108     explicit FooBar(TemporaryFile&& f) : f_(std::move(f)) {}
109   };
110   TemporaryFile g("Foo");
111   FooBar fb(std::move(g));
112   EXPECT_EQ(g.fd(), -1);
113   EXPECT_NE(fb.f_.fd(), -1);
114 }
115 
testTemporaryDirectory(TemporaryDirectory::Scope scope)116 void testTemporaryDirectory(TemporaryDirectory::Scope scope) {
117   fs::path path;
118   {
119     TemporaryDirectory d("", "", scope);
120     path = d.path();
121     EXPECT_FALSE(path.empty());
122     EXPECT_TRUE(path.is_absolute());
123     EXPECT_TRUE(fs::exists(path));
124     EXPECT_TRUE(fs::is_directory(path));
125 
126     fs::path fp = path / "bar";
127     int fd = open(fp.string().c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
128     EXPECT_NE(fd, -1);
129     close(fd);
130 
131     TemporaryFile f("Foo", d.path());
132     EXPECT_EQ(d.path(), f.path().parent_path());
133   }
134   bool exists = (scope == TemporaryDirectory::Scope::PERMANENT);
135   EXPECT_EQ(exists, fs::exists(path));
136 }
137 
TEST(TemporaryDirectory,Permanent)138 TEST(TemporaryDirectory, Permanent) {
139   testTemporaryDirectory(TemporaryDirectory::Scope::PERMANENT);
140 }
141 
TEST(TemporaryDirectory,DeleteOnDestruction)142 TEST(TemporaryDirectory, DeleteOnDestruction) {
143   testTemporaryDirectory(TemporaryDirectory::Scope::DELETE_ON_DESTRUCTION);
144 }
145 
expectTempdirExists(const TemporaryDirectory & d)146 void expectTempdirExists(const TemporaryDirectory& d) {
147   EXPECT_FALSE(d.path().empty());
148   EXPECT_TRUE(fs::exists(d.path()));
149   EXPECT_TRUE(fs::is_directory(d.path()));
150 }
151 
TEST(TemporaryDirectory,SafelyMove)152 TEST(TemporaryDirectory, SafelyMove) {
153   std::unique_ptr<TemporaryDirectory> dir;
154   TemporaryDirectory dir2;
155   {
156     auto scope = TemporaryDirectory::Scope::DELETE_ON_DESTRUCTION;
157     TemporaryDirectory d("", "", scope);
158     TemporaryDirectory d2("", "", scope);
159     expectTempdirExists(d);
160     expectTempdirExists(d2);
161 
162     dir = std::make_unique<TemporaryDirectory>(std::move(d));
163     dir2 = std::move(d2);
164   }
165 
166   expectTempdirExists(*dir);
167   expectTempdirExists(dir2);
168 }
169 
TEST(ChangeToTempDir,ChangeDir)170 TEST(ChangeToTempDir, ChangeDir) {
171   auto pwd1 = fs::current_path();
172   {
173     ChangeToTempDir d;
174     EXPECT_NE(pwd1, fs::current_path());
175   }
176   EXPECT_EQ(pwd1, fs::current_path());
177 }
178 
TEST(PCREPatternMatch,Simple)179 TEST(PCREPatternMatch, Simple) {
180   EXPECT_PCRE_MATCH(".*a.c.*", "gabca");
181   EXPECT_NO_PCRE_MATCH("a.c", "gabca");
182   EXPECT_NO_PCRE_MATCH(".*ac.*", "gabca");
183 }
184 
TEST(CaptureFD,GlogPatterns)185 TEST(CaptureFD, GlogPatterns) {
186   CaptureFD err(fileno(stderr));
187   LOG(INFO) << "All is well";
188   EXPECT_NO_PCRE_MATCH(glogErrOrWarnPattern(), err.readIncremental());
189   {
190     LOG(ERROR) << "Uh-oh";
191     auto s = err.readIncremental();
192     EXPECT_PCRE_MATCH(glogErrorPattern(), s);
193     EXPECT_NO_PCRE_MATCH(glogWarningPattern(), s);
194     EXPECT_PCRE_MATCH(glogErrOrWarnPattern(), s);
195   }
196   {
197     LOG(WARNING) << "Oops";
198     auto s = err.readIncremental();
199     EXPECT_NO_PCRE_MATCH(glogErrorPattern(), s);
200     EXPECT_PCRE_MATCH(glogWarningPattern(), s);
201     EXPECT_PCRE_MATCH(glogErrOrWarnPattern(), s);
202   }
203 }
204 
TEST(CaptureFD,ChunkCob)205 TEST(CaptureFD, ChunkCob) {
206   std::vector<std::string> chunks;
207   {
208     CaptureFD err(fileno(stderr), [&](StringPiece p) {
209       chunks.emplace_back(p.str());
210       switch (chunks.size()) {
211         case 1:
212           EXPECT_PCRE_MATCH(".*foo.*bar.*", p);
213           break;
214         case 2:
215           EXPECT_PCRE_MATCH("[^\n]*baz.*", p);
216           break;
217         default:
218           FAIL() << "Got too many chunks: " << chunks.size();
219       }
220     });
221     LOG(INFO) << "foo";
222     LOG(INFO) << "bar";
223     EXPECT_PCRE_MATCH(".*foo.*bar.*", err.read());
224     auto chunk = err.readIncremental();
225     EXPECT_EQ(chunks.at(0), chunk);
226     LOG(INFO) << "baz";
227     EXPECT_PCRE_MATCH(".*foo.*bar.*baz.*", err.read());
228   }
229   EXPECT_EQ(2, chunks.size());
230 }
231