/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/testing/TestUtil.h>
#include <system_error>
#include <boost/algorithm/string.hpp>
#include <glog/logging.h>
#include <folly/Memory.h>
#include <folly/portability/Fcntl.h>
#include <folly/portability/GTest.h>
#include <folly/portability/Stdlib.h>
using namespace folly;
using namespace folly::test;
TEST(TemporaryFile, Simple) {
int fd = -1;
char c = 'x';
{
TemporaryFile f;
EXPECT_FALSE(f.path().empty());
EXPECT_TRUE(f.path().is_absolute());
fd = f.fd();
EXPECT_LE(0, fd);
ssize_t r = write(fd, &c, 1);
EXPECT_EQ(1, r);
}
msvcSuppressAbortOnInvalidParams([&] {
// The file must have been closed. This assumes that no other thread
// has opened another file in the meanwhile, which is a sane assumption
// to make in this test.
ssize_t r = write(fd, &c, 1);
int savedErrno = errno;
EXPECT_EQ(-1, r);
EXPECT_EQ(EBADF, savedErrno);
});
}
TEST(TemporaryFile, EarlyClose) {
fs::path p;
{
TemporaryFile f;
p = f.path();
EXPECT_TRUE(fs::exists(p));
f.close();
EXPECT_EQ(-1, f.fd());
EXPECT_TRUE(fs::exists(p));
}
EXPECT_FALSE(fs::exists(p));
}
TEST(TemporaryFile, Prefix) {
TemporaryFile f("Foo");
EXPECT_TRUE(f.path().is_absolute());
EXPECT_TRUE(
boost::algorithm::starts_with(f.path().filename().native(), "Foo"));
}
TEST(TemporaryFile, PathPrefix) {
TemporaryFile f("Foo", ".");
EXPECT_EQ(fs::path("."), f.path().parent_path());
EXPECT_TRUE(
boost::algorithm::starts_with(f.path().filename().native(), "Foo"));
}
TEST(TemporaryFile, NoSuchPath) {
EXPECT_THROW({ TemporaryFile f("", "/no/such/path"); }, std::system_error);
}
TEST(TemporaryFile, moveAssignment) {
TemporaryFile f;
int fd;
EXPECT_TRUE(f.path().is_absolute());
{
TemporaryFile g("Foo", ".");
EXPECT_NE(g.fd(), -1);
fd = g.fd();
f = std::move(g);
}
EXPECT_TRUE(fs::exists(f.path()));
EXPECT_EQ(fs::path("."), f.path().parent_path());
EXPECT_EQ(f.fd(), fd);
TemporaryFile h = TemporaryFile("FooBar", ".");
EXPECT_NE(h.fd(), -1);
}
TEST(TemporaryFile, moveCtor) {
struct FooBar {
TemporaryFile f_;
explicit FooBar(TemporaryFile&& f) : f_(std::move(f)) {}
};
TemporaryFile g("Foo");
FooBar fb(std::move(g));
EXPECT_EQ(g.fd(), -1);
EXPECT_NE(fb.f_.fd(), -1);
}
void testTemporaryDirectory(TemporaryDirectory::Scope scope) {
fs::path path;
{
TemporaryDirectory d("", "", scope);
path = d.path();
EXPECT_FALSE(path.empty());
EXPECT_TRUE(path.is_absolute());
EXPECT_TRUE(fs::exists(path));
EXPECT_TRUE(fs::is_directory(path));
fs::path fp = path / "bar";
int fd = open(fp.string().c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
EXPECT_NE(fd, -1);
close(fd);
TemporaryFile f("Foo", d.path());
EXPECT_EQ(d.path(), f.path().parent_path());
}
bool exists = (scope == TemporaryDirectory::Scope::PERMANENT);
EXPECT_EQ(exists, fs::exists(path));
}
TEST(TemporaryDirectory, Permanent) {
testTemporaryDirectory(TemporaryDirectory::Scope::PERMANENT);
}
TEST(TemporaryDirectory, DeleteOnDestruction) {
testTemporaryDirectory(TemporaryDirectory::Scope::DELETE_ON_DESTRUCTION);
}
void expectTempdirExists(const TemporaryDirectory& d) {
EXPECT_FALSE(d.path().empty());
EXPECT_TRUE(fs::exists(d.path()));
EXPECT_TRUE(fs::is_directory(d.path()));
}
TEST(TemporaryDirectory, SafelyMove) {
std::unique_ptr<TemporaryDirectory> dir;
TemporaryDirectory dir2;
{
auto scope = TemporaryDirectory::Scope::DELETE_ON_DESTRUCTION;
TemporaryDirectory d("", "", scope);
TemporaryDirectory d2("", "", scope);
expectTempdirExists(d);
expectTempdirExists(d2);
dir = std::make_unique<TemporaryDirectory>(std::move(d));
dir2 = std::move(d2);
}
expectTempdirExists(*dir);
expectTempdirExists(dir2);
}
TEST(ChangeToTempDir, ChangeDir) {
auto pwd1 = fs::current_path();
{
ChangeToTempDir d;
EXPECT_NE(pwd1, fs::current_path());
}
EXPECT_EQ(pwd1, fs::current_path());
}
TEST(PCREPatternMatch, Simple) {
EXPECT_PCRE_MATCH(".*a.c.*", "gabca");
EXPECT_NO_PCRE_MATCH("a.c", "gabca");
EXPECT_NO_PCRE_MATCH(".*ac.*", "gabca");
}
TEST(CaptureFD, GlogPatterns) {
CaptureFD err(fileno(stderr));
LOG(INFO) << "All is well";
EXPECT_NO_PCRE_MATCH(glogErrOrWarnPattern(), err.readIncremental());
{
LOG(ERROR) << "Uh-oh";
auto s = err.readIncremental();
EXPECT_PCRE_MATCH(glogErrorPattern(), s);
EXPECT_NO_PCRE_MATCH(glogWarningPattern(), s);
EXPECT_PCRE_MATCH(glogErrOrWarnPattern(), s);
}
{
LOG(WARNING) << "Oops";
auto s = err.readIncremental();
EXPECT_NO_PCRE_MATCH(glogErrorPattern(), s);
EXPECT_PCRE_MATCH(glogWarningPattern(), s);
EXPECT_PCRE_MATCH(glogErrOrWarnPattern(), s);
}
}
TEST(CaptureFD, ChunkCob) {
std::vector<std::string> chunks;
{
CaptureFD err(fileno(stderr), [&](StringPiece p) {
chunks.emplace_back(p.str());
switch (chunks.size()) {
case 1:
EXPECT_PCRE_MATCH(".*foo.*bar.*", p);
break;
case 2:
EXPECT_PCRE_MATCH("[^\n]*baz.*", p);
break;
default:
FAIL() << "Got too many chunks: " << chunks.size();
}
});
LOG(INFO) << "foo";
LOG(INFO) << "bar";
EXPECT_PCRE_MATCH(".*foo.*bar.*", err.read());
auto chunk = err.readIncremental();
EXPECT_EQ(chunks.at(0), chunk);
LOG(INFO) << "baz";
EXPECT_PCRE_MATCH(".*foo.*bar.*baz.*", err.read());
}
EXPECT_EQ(2, chunks.size());
}