2010-11-29 23:29:04 +01:00
|
|
|
//===- llvm/unittest/Support/Path.cpp - Path tests ------------------------===//
|
2010-11-24 20:20:05 +01:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2013-06-12 00:21:28 +02:00
|
|
|
#include "llvm/Support/Path.h"
|
2016-08-12 00:21:41 +02:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2017-06-06 13:06:56 +02:00
|
|
|
#include "llvm/ADT/SmallVector.h"
|
2017-02-22 15:34:45 +01:00
|
|
|
#include "llvm/ADT/Triple.h"
|
2017-06-07 05:48:56 +02:00
|
|
|
#include "llvm/BinaryFormat/Magic.h"
|
2018-04-30 16:59:11 +02:00
|
|
|
#include "llvm/Config/llvm-config.h"
|
2015-10-16 11:08:59 +02:00
|
|
|
#include "llvm/Support/ConvertUTF.h"
|
2014-06-13 19:20:48 +02:00
|
|
|
#include "llvm/Support/Errc.h"
|
2010-12-06 05:28:42 +01:00
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
2012-12-04 11:23:08 +01:00
|
|
|
#include "llvm/Support/FileSystem.h"
|
2016-09-02 02:51:34 +02:00
|
|
|
#include "llvm/Support/FileUtilities.h"
|
2017-02-22 15:34:45 +01:00
|
|
|
#include "llvm/Support/Host.h"
|
2013-07-19 16:41:25 +02:00
|
|
|
#include "llvm/Support/MemoryBuffer.h"
|
2011-01-06 06:57:54 +01:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2010-11-24 20:20:05 +01:00
|
|
|
#include "gtest/gtest.h"
|
2018-04-24 10:29:20 +02:00
|
|
|
#include "gmock/gmock.h"
|
2010-11-24 20:20:05 +01:00
|
|
|
|
2018-04-29 02:45:03 +02:00
|
|
|
#ifdef _WIN32
|
2016-04-18 15:54:50 +02:00
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
2018-07-04 16:17:10 +02:00
|
|
|
#include "llvm/Support/Chrono.h"
|
2015-06-16 08:46:16 +02:00
|
|
|
#include <windows.h>
|
2014-06-11 05:58:34 +02:00
|
|
|
#include <winerror.h>
|
|
|
|
#endif
|
|
|
|
|
2015-08-06 23:04:55 +02:00
|
|
|
#ifdef LLVM_ON_UNIX
|
2017-03-22 17:30:06 +01:00
|
|
|
#include <pwd.h>
|
2015-08-06 23:04:55 +02:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#endif
|
|
|
|
|
2010-11-29 23:28:51 +01:00
|
|
|
using namespace llvm;
|
2010-12-03 02:21:28 +01:00
|
|
|
using namespace llvm::sys;
|
2014-06-13 05:20:08 +02:00
|
|
|
|
|
|
|
#define ASSERT_NO_ERROR(x) \
|
|
|
|
if (std::error_code ASSERT_NO_ERROR_ec = x) { \
|
|
|
|
SmallString<128> MessageStorage; \
|
|
|
|
raw_svector_ostream Message(MessageStorage); \
|
|
|
|
Message << #x ": did not return errc::success.\n" \
|
|
|
|
<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \
|
|
|
|
<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \
|
|
|
|
GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \
|
|
|
|
} else { \
|
|
|
|
}
|
2011-01-04 18:00:18 +01:00
|
|
|
|
2018-06-07 21:58:58 +02:00
|
|
|
#define ASSERT_ERROR(x) \
|
|
|
|
if (!x) { \
|
|
|
|
SmallString<128> MessageStorage; \
|
|
|
|
raw_svector_ostream Message(MessageStorage); \
|
|
|
|
Message << #x ": did not return a failure error code.\n"; \
|
|
|
|
GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \
|
|
|
|
}
|
|
|
|
|
2010-11-24 20:20:05 +01:00
|
|
|
namespace {
|
|
|
|
|
2018-06-07 21:58:58 +02:00
|
|
|
struct FileDescriptorCloser {
|
|
|
|
explicit FileDescriptorCloser(int FD) : FD(FD) {}
|
|
|
|
~FileDescriptorCloser() { ::close(FD); }
|
|
|
|
int FD;
|
|
|
|
};
|
|
|
|
|
2011-02-11 22:24:40 +01:00
|
|
|
TEST(is_separator, Works) {
|
|
|
|
EXPECT_TRUE(path::is_separator('/'));
|
|
|
|
EXPECT_FALSE(path::is_separator('\0'));
|
|
|
|
EXPECT_FALSE(path::is_separator('-'));
|
|
|
|
EXPECT_FALSE(path::is_separator(' '));
|
|
|
|
|
2017-03-16 23:28:04 +01:00
|
|
|
EXPECT_TRUE(path::is_separator('\\', path::Style::windows));
|
|
|
|
EXPECT_FALSE(path::is_separator('\\', path::Style::posix));
|
|
|
|
|
2018-04-29 02:45:03 +02:00
|
|
|
#ifdef _WIN32
|
2011-02-11 22:24:40 +01:00
|
|
|
EXPECT_TRUE(path::is_separator('\\'));
|
|
|
|
#else
|
2017-03-17 00:19:40 +01:00
|
|
|
EXPECT_FALSE(path::is_separator('\\'));
|
2011-02-11 22:24:40 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2010-11-29 23:29:04 +01:00
|
|
|
TEST(Support, Path) {
|
2010-11-29 23:28:51 +01:00
|
|
|
SmallVector<StringRef, 40> paths;
|
|
|
|
paths.push_back("");
|
|
|
|
paths.push_back(".");
|
|
|
|
paths.push_back("..");
|
|
|
|
paths.push_back("foo");
|
|
|
|
paths.push_back("/");
|
|
|
|
paths.push_back("/foo");
|
|
|
|
paths.push_back("foo/");
|
|
|
|
paths.push_back("/foo/");
|
|
|
|
paths.push_back("foo/bar");
|
|
|
|
paths.push_back("/foo/bar");
|
|
|
|
paths.push_back("//net");
|
2018-04-24 10:29:20 +02:00
|
|
|
paths.push_back("//net/");
|
2010-11-29 23:28:51 +01:00
|
|
|
paths.push_back("//net/foo");
|
|
|
|
paths.push_back("///foo///");
|
|
|
|
paths.push_back("///foo///bar");
|
|
|
|
paths.push_back("/.");
|
|
|
|
paths.push_back("./");
|
|
|
|
paths.push_back("/..");
|
|
|
|
paths.push_back("../");
|
|
|
|
paths.push_back("foo/.");
|
|
|
|
paths.push_back("foo/..");
|
|
|
|
paths.push_back("foo/./");
|
|
|
|
paths.push_back("foo/./bar");
|
|
|
|
paths.push_back("foo/..");
|
|
|
|
paths.push_back("foo/../");
|
|
|
|
paths.push_back("foo/../bar");
|
|
|
|
paths.push_back("c:");
|
|
|
|
paths.push_back("c:/");
|
|
|
|
paths.push_back("c:foo");
|
|
|
|
paths.push_back("c:/foo");
|
|
|
|
paths.push_back("c:foo/");
|
|
|
|
paths.push_back("c:/foo/");
|
|
|
|
paths.push_back("c:/foo/bar");
|
|
|
|
paths.push_back("prn:");
|
|
|
|
paths.push_back("c:\\");
|
|
|
|
paths.push_back("c:foo");
|
|
|
|
paths.push_back("c:\\foo");
|
|
|
|
paths.push_back("c:foo\\");
|
|
|
|
paths.push_back("c:\\foo\\");
|
|
|
|
paths.push_back("c:\\foo/");
|
|
|
|
paths.push_back("c:/foo\\bar");
|
|
|
|
|
|
|
|
for (SmallVector<StringRef, 40>::const_iterator i = paths.begin(),
|
|
|
|
e = paths.end();
|
|
|
|
i != e;
|
|
|
|
++i) {
|
2018-04-24 10:29:20 +02:00
|
|
|
SCOPED_TRACE(*i);
|
|
|
|
SmallVector<StringRef, 5> ComponentStack;
|
2010-11-29 23:28:51 +01:00
|
|
|
for (sys::path::const_iterator ci = sys::path::begin(*i),
|
|
|
|
ce = sys::path::end(*i);
|
|
|
|
ci != ce;
|
|
|
|
++ci) {
|
2018-04-24 10:29:20 +02:00
|
|
|
EXPECT_FALSE(ci->empty());
|
2014-07-16 10:18:58 +02:00
|
|
|
ComponentStack.push_back(*ci);
|
2010-11-29 23:28:51 +01:00
|
|
|
}
|
|
|
|
|
2018-04-24 10:29:20 +02:00
|
|
|
SmallVector<StringRef, 5> ReverseComponentStack;
|
2010-12-01 00:28:07 +01:00
|
|
|
for (sys::path::reverse_iterator ci = sys::path::rbegin(*i),
|
|
|
|
ce = sys::path::rend(*i);
|
|
|
|
ci != ce;
|
|
|
|
++ci) {
|
2018-04-24 10:29:20 +02:00
|
|
|
EXPECT_FALSE(ci->empty());
|
|
|
|
ReverseComponentStack.push_back(*ci);
|
2010-12-01 00:28:07 +01:00
|
|
|
}
|
2018-04-24 10:29:20 +02:00
|
|
|
std::reverse(ReverseComponentStack.begin(), ReverseComponentStack.end());
|
|
|
|
EXPECT_THAT(ComponentStack, testing::ContainerEq(ReverseComponentStack));
|
2010-12-01 00:28:07 +01:00
|
|
|
|
2016-10-17 00:09:24 +02:00
|
|
|
// Crash test most of the API - since we're iterating over all of our paths
|
|
|
|
// here there isn't really anything reasonable to assert on in the results.
|
|
|
|
(void)path::has_root_path(*i);
|
|
|
|
(void)path::root_path(*i);
|
|
|
|
(void)path::has_root_name(*i);
|
|
|
|
(void)path::root_name(*i);
|
|
|
|
(void)path::has_root_directory(*i);
|
|
|
|
(void)path::root_directory(*i);
|
|
|
|
(void)path::has_parent_path(*i);
|
|
|
|
(void)path::parent_path(*i);
|
|
|
|
(void)path::has_filename(*i);
|
|
|
|
(void)path::filename(*i);
|
|
|
|
(void)path::has_stem(*i);
|
|
|
|
(void)path::stem(*i);
|
|
|
|
(void)path::has_extension(*i);
|
|
|
|
(void)path::extension(*i);
|
|
|
|
(void)path::is_absolute(*i);
|
|
|
|
(void)path::is_relative(*i);
|
2010-12-01 07:03:33 +01:00
|
|
|
|
2011-01-04 18:00:18 +01:00
|
|
|
SmallString<128> temp_store;
|
2010-12-03 03:22:34 +01:00
|
|
|
temp_store = *i;
|
2011-01-04 18:00:18 +01:00
|
|
|
ASSERT_NO_ERROR(fs::make_absolute(temp_store));
|
2010-12-03 03:22:34 +01:00
|
|
|
temp_store = *i;
|
2010-12-07 04:57:37 +01:00
|
|
|
path::remove_filename(temp_store);
|
2010-12-01 07:03:33 +01:00
|
|
|
|
2010-12-03 03:22:34 +01:00
|
|
|
temp_store = *i;
|
2010-12-07 04:57:37 +01:00
|
|
|
path::replace_extension(temp_store, "ext");
|
2010-12-01 07:03:33 +01:00
|
|
|
StringRef filename(temp_store.begin(), temp_store.size()), stem, ext;
|
2010-12-07 18:04:04 +01:00
|
|
|
stem = path::stem(filename);
|
|
|
|
ext = path::extension(filename);
|
2014-08-04 19:36:41 +02:00
|
|
|
EXPECT_EQ(*sys::path::rbegin(filename), (stem + ext).str());
|
2010-12-01 07:03:33 +01:00
|
|
|
|
2010-12-07 04:57:37 +01:00
|
|
|
path::native(*i, temp_store);
|
2010-11-29 23:28:51 +01:00
|
|
|
}
|
2015-10-05 15:02:43 +02:00
|
|
|
|
|
|
|
SmallString<32> Relative("foo.cpp");
|
|
|
|
ASSERT_NO_ERROR(sys::fs::make_absolute("/root", Relative));
|
2015-10-05 16:15:13 +02:00
|
|
|
Relative[5] = '/'; // Fix up windows paths.
|
2015-10-05 15:02:43 +02:00
|
|
|
ASSERT_EQ("/root/foo.cpp", Relative);
|
2011-01-05 17:39:05 +01:00
|
|
|
}
|
|
|
|
|
2018-04-24 10:29:20 +02:00
|
|
|
TEST(Support, FilenameParent) {
|
|
|
|
EXPECT_EQ("/", path::filename("/"));
|
|
|
|
EXPECT_EQ("", path::parent_path("/"));
|
2013-08-12 19:10:49 +02:00
|
|
|
|
2018-04-24 10:29:20 +02:00
|
|
|
EXPECT_EQ("\\", path::filename("c:\\", path::Style::windows));
|
|
|
|
EXPECT_EQ("c:", path::parent_path("c:\\", path::Style::windows));
|
2013-08-12 19:10:49 +02:00
|
|
|
|
[Support/Path] Make handling of paths like "///" consistent
Summary:
Various path functions were not treating paths consisting of slashes
alone consistently. For example, the iterator-based accessors decomposed the
path "///" into two elements: "/" and ".". This is not too bad, but it
is different from the behavior specified by posix:
```
A pathname that contains ***at least one non-slash character*** and that
ends with one or more trailing slashes shall be resolved as if a single
dot character ( '.' ) were appended to the pathname.
```
More importantly, this was different from how we treated the same path
in the filename+parent_path functions, which decomposed this path into
"." and "". This was completely wrong as it lost the information that
this was an absolute path which referred to the root directory.
This patch fixes this behavior by making sure all functions treat paths
consisting of (back)slashes alone the same way as "/". I.e., the
iterator-based functions will just report one component ("/"), and the
filename+parent_path will decompose them into "/" and "".
A slightly controversial topic here may be the treatment of "//". Posix
says that paths beginning with "//" may have special meaning and indeed
we have code which parses paths like "//net/foo/bar" specially. However,
as we were already not being consistent in parsing the "//" string
alone, and any special parsing for it would complicate the code further,
I chose to treat it the same way as longer sequences of slashes (which
are guaranteed to be the same as "/").
Another slight change of behavior is in the parsing of paths like
"//net//". Previously the last component of this path was ".". However,
as in our parsing the "//net" part in this path was the same as the
"drive" part in "c:\" and the next slash was the "root directory", it
made sense to treat "//net//" the same way as "//net/" (i.e., not to add
the extra "." component at the end).
Reviewers: zturner, rnk, dblaikie, Bigcheese
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D45942
llvm-svn: 331876
2018-05-09 15:21:16 +02:00
|
|
|
EXPECT_EQ("/", path::filename("///"));
|
|
|
|
EXPECT_EQ("", path::parent_path("///"));
|
|
|
|
|
|
|
|
EXPECT_EQ("\\", path::filename("c:\\\\", path::Style::windows));
|
|
|
|
EXPECT_EQ("c:", path::parent_path("c:\\\\", path::Style::windows));
|
|
|
|
|
2018-04-24 10:29:20 +02:00
|
|
|
EXPECT_EQ("bar", path::filename("/foo/bar"));
|
|
|
|
EXPECT_EQ("/foo", path::parent_path("/foo/bar"));
|
2013-08-12 19:10:49 +02:00
|
|
|
|
2018-04-24 10:29:20 +02:00
|
|
|
EXPECT_EQ("foo", path::filename("/foo"));
|
|
|
|
EXPECT_EQ("/", path::parent_path("/foo"));
|
2013-08-12 19:10:49 +02:00
|
|
|
|
2018-04-24 10:29:20 +02:00
|
|
|
EXPECT_EQ("foo", path::filename("foo"));
|
|
|
|
EXPECT_EQ("", path::parent_path("foo"));
|
2015-03-10 01:04:29 +01:00
|
|
|
|
2018-04-24 10:29:20 +02:00
|
|
|
EXPECT_EQ(".", path::filename("foo/"));
|
|
|
|
EXPECT_EQ("foo", path::parent_path("foo/"));
|
2015-03-10 01:04:29 +01:00
|
|
|
|
2018-04-24 10:29:20 +02:00
|
|
|
EXPECT_EQ("//net", path::filename("//net"));
|
|
|
|
EXPECT_EQ("", path::parent_path("//net"));
|
2015-03-10 01:04:29 +01:00
|
|
|
|
2018-04-24 10:29:20 +02:00
|
|
|
EXPECT_EQ("/", path::filename("//net/"));
|
|
|
|
EXPECT_EQ("//net", path::parent_path("//net/"));
|
2015-03-10 01:04:29 +01:00
|
|
|
|
2018-04-24 10:29:20 +02:00
|
|
|
EXPECT_EQ("foo", path::filename("//net/foo"));
|
|
|
|
EXPECT_EQ("//net/", path::parent_path("//net/foo"));
|
[Support/Path] Make handling of paths like "///" consistent
Summary:
Various path functions were not treating paths consisting of slashes
alone consistently. For example, the iterator-based accessors decomposed the
path "///" into two elements: "/" and ".". This is not too bad, but it
is different from the behavior specified by posix:
```
A pathname that contains ***at least one non-slash character*** and that
ends with one or more trailing slashes shall be resolved as if a single
dot character ( '.' ) were appended to the pathname.
```
More importantly, this was different from how we treated the same path
in the filename+parent_path functions, which decomposed this path into
"." and "". This was completely wrong as it lost the information that
this was an absolute path which referred to the root directory.
This patch fixes this behavior by making sure all functions treat paths
consisting of (back)slashes alone the same way as "/". I.e., the
iterator-based functions will just report one component ("/"), and the
filename+parent_path will decompose them into "/" and "".
A slightly controversial topic here may be the treatment of "//". Posix
says that paths beginning with "//" may have special meaning and indeed
we have code which parses paths like "//net/foo/bar" specially. However,
as we were already not being consistent in parsing the "//" string
alone, and any special parsing for it would complicate the code further,
I chose to treat it the same way as longer sequences of slashes (which
are guaranteed to be the same as "/").
Another slight change of behavior is in the parsing of paths like
"//net//". Previously the last component of this path was ".". However,
as in our parsing the "//net" part in this path was the same as the
"drive" part in "c:\" and the next slash was the "root directory", it
made sense to treat "//net//" the same way as "//net/" (i.e., not to add
the extra "." component at the end).
Reviewers: zturner, rnk, dblaikie, Bigcheese
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D45942
llvm-svn: 331876
2018-05-09 15:21:16 +02:00
|
|
|
|
|
|
|
// These checks are just to make sure we do something reasonable with the
|
|
|
|
// paths below. They are not meant to prescribe the one true interpretation of
|
|
|
|
// these paths. Other decompositions (e.g. "//" -> "" + "//") are also
|
|
|
|
// possible.
|
|
|
|
EXPECT_EQ("/", path::filename("//"));
|
|
|
|
EXPECT_EQ("", path::parent_path("//"));
|
|
|
|
|
|
|
|
EXPECT_EQ("\\", path::filename("\\\\", path::Style::windows));
|
|
|
|
EXPECT_EQ("", path::parent_path("\\\\", path::Style::windows));
|
|
|
|
|
|
|
|
EXPECT_EQ("\\", path::filename("\\\\\\", path::Style::windows));
|
|
|
|
EXPECT_EQ("", path::parent_path("\\\\\\", path::Style::windows));
|
2015-03-10 01:04:29 +01:00
|
|
|
}
|
|
|
|
|
2018-04-24 10:29:20 +02:00
|
|
|
static std::vector<StringRef>
|
|
|
|
GetComponents(StringRef Path, path::Style S = path::Style::native) {
|
|
|
|
return {path::begin(Path, S), path::end(Path)};
|
2015-03-10 01:04:29 +01:00
|
|
|
}
|
|
|
|
|
2018-04-24 10:29:20 +02:00
|
|
|
TEST(Support, PathIterator) {
|
|
|
|
EXPECT_THAT(GetComponents("/foo"), testing::ElementsAre("/", "foo"));
|
|
|
|
EXPECT_THAT(GetComponents("/"), testing::ElementsAre("/"));
|
[Support/Path] Make handling of paths like "///" consistent
Summary:
Various path functions were not treating paths consisting of slashes
alone consistently. For example, the iterator-based accessors decomposed the
path "///" into two elements: "/" and ".". This is not too bad, but it
is different from the behavior specified by posix:
```
A pathname that contains ***at least one non-slash character*** and that
ends with one or more trailing slashes shall be resolved as if a single
dot character ( '.' ) were appended to the pathname.
```
More importantly, this was different from how we treated the same path
in the filename+parent_path functions, which decomposed this path into
"." and "". This was completely wrong as it lost the information that
this was an absolute path which referred to the root directory.
This patch fixes this behavior by making sure all functions treat paths
consisting of (back)slashes alone the same way as "/". I.e., the
iterator-based functions will just report one component ("/"), and the
filename+parent_path will decompose them into "/" and "".
A slightly controversial topic here may be the treatment of "//". Posix
says that paths beginning with "//" may have special meaning and indeed
we have code which parses paths like "//net/foo/bar" specially. However,
as we were already not being consistent in parsing the "//" string
alone, and any special parsing for it would complicate the code further,
I chose to treat it the same way as longer sequences of slashes (which
are guaranteed to be the same as "/").
Another slight change of behavior is in the parsing of paths like
"//net//". Previously the last component of this path was ".". However,
as in our parsing the "//net" part in this path was the same as the
"drive" part in "c:\" and the next slash was the "root directory", it
made sense to treat "//net//" the same way as "//net/" (i.e., not to add
the extra "." component at the end).
Reviewers: zturner, rnk, dblaikie, Bigcheese
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D45942
llvm-svn: 331876
2018-05-09 15:21:16 +02:00
|
|
|
EXPECT_THAT(GetComponents("//"), testing::ElementsAre("/"));
|
|
|
|
EXPECT_THAT(GetComponents("///"), testing::ElementsAre("/"));
|
2018-04-24 10:29:20 +02:00
|
|
|
EXPECT_THAT(GetComponents("c/d/e/foo.txt"),
|
|
|
|
testing::ElementsAre("c", "d", "e", "foo.txt"));
|
|
|
|
EXPECT_THAT(GetComponents(".c/.d/../."),
|
|
|
|
testing::ElementsAre(".c", ".d", "..", "."));
|
|
|
|
EXPECT_THAT(GetComponents("/c/d/e/foo.txt"),
|
|
|
|
testing::ElementsAre("/", "c", "d", "e", "foo.txt"));
|
|
|
|
EXPECT_THAT(GetComponents("/.c/.d/../."),
|
|
|
|
testing::ElementsAre("/", ".c", ".d", "..", "."));
|
|
|
|
EXPECT_THAT(GetComponents("c:\\c\\e\\foo.txt", path::Style::windows),
|
|
|
|
testing::ElementsAre("c:", "\\", "c", "e", "foo.txt"));
|
|
|
|
EXPECT_THAT(GetComponents("//net/"), testing::ElementsAre("//net", "/"));
|
|
|
|
EXPECT_THAT(GetComponents("//net/c/foo.txt"),
|
|
|
|
testing::ElementsAre("//net", "/", "c", "foo.txt"));
|
2013-08-12 19:10:49 +02:00
|
|
|
}
|
|
|
|
|
2014-03-05 20:56:30 +01:00
|
|
|
TEST(Support, AbsolutePathIteratorEnd) {
|
|
|
|
// Trailing slashes are converted to '.' unless they are part of the root path.
|
2017-03-16 23:28:04 +01:00
|
|
|
SmallVector<std::pair<StringRef, path::Style>, 4> Paths;
|
|
|
|
Paths.emplace_back("/foo/", path::Style::native);
|
|
|
|
Paths.emplace_back("/foo//", path::Style::native);
|
2018-04-24 10:29:20 +02:00
|
|
|
Paths.emplace_back("//net/foo/", path::Style::native);
|
|
|
|
Paths.emplace_back("c:\\foo\\", path::Style::windows);
|
2017-03-16 23:28:04 +01:00
|
|
|
|
|
|
|
for (auto &Path : Paths) {
|
2018-04-24 10:29:20 +02:00
|
|
|
SCOPED_TRACE(Path.first);
|
2017-03-16 23:28:04 +01:00
|
|
|
StringRef LastComponent = *path::rbegin(Path.first, Path.second);
|
2014-03-05 20:56:30 +01:00
|
|
|
EXPECT_EQ(".", LastComponent);
|
|
|
|
}
|
|
|
|
|
2017-03-16 23:28:04 +01:00
|
|
|
SmallVector<std::pair<StringRef, path::Style>, 3> RootPaths;
|
|
|
|
RootPaths.emplace_back("/", path::Style::native);
|
|
|
|
RootPaths.emplace_back("//net/", path::Style::native);
|
|
|
|
RootPaths.emplace_back("c:\\", path::Style::windows);
|
[Support/Path] Make handling of paths like "///" consistent
Summary:
Various path functions were not treating paths consisting of slashes
alone consistently. For example, the iterator-based accessors decomposed the
path "///" into two elements: "/" and ".". This is not too bad, but it
is different from the behavior specified by posix:
```
A pathname that contains ***at least one non-slash character*** and that
ends with one or more trailing slashes shall be resolved as if a single
dot character ( '.' ) were appended to the pathname.
```
More importantly, this was different from how we treated the same path
in the filename+parent_path functions, which decomposed this path into
"." and "". This was completely wrong as it lost the information that
this was an absolute path which referred to the root directory.
This patch fixes this behavior by making sure all functions treat paths
consisting of (back)slashes alone the same way as "/". I.e., the
iterator-based functions will just report one component ("/"), and the
filename+parent_path will decompose them into "/" and "".
A slightly controversial topic here may be the treatment of "//". Posix
says that paths beginning with "//" may have special meaning and indeed
we have code which parses paths like "//net/foo/bar" specially. However,
as we were already not being consistent in parsing the "//" string
alone, and any special parsing for it would complicate the code further,
I chose to treat it the same way as longer sequences of slashes (which
are guaranteed to be the same as "/").
Another slight change of behavior is in the parsing of paths like
"//net//". Previously the last component of this path was ".". However,
as in our parsing the "//net" part in this path was the same as the
"drive" part in "c:\" and the next slash was the "root directory", it
made sense to treat "//net//" the same way as "//net/" (i.e., not to add
the extra "." component at the end).
Reviewers: zturner, rnk, dblaikie, Bigcheese
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D45942
llvm-svn: 331876
2018-05-09 15:21:16 +02:00
|
|
|
RootPaths.emplace_back("//net//", path::Style::native);
|
|
|
|
RootPaths.emplace_back("c:\\\\", path::Style::windows);
|
2014-03-05 20:56:30 +01:00
|
|
|
|
2017-03-16 23:28:04 +01:00
|
|
|
for (auto &Path : RootPaths) {
|
2018-04-24 10:29:20 +02:00
|
|
|
SCOPED_TRACE(Path.first);
|
2017-03-16 23:28:04 +01:00
|
|
|
StringRef LastComponent = *path::rbegin(Path.first, Path.second);
|
2014-03-05 20:56:30 +01:00
|
|
|
EXPECT_EQ(1u, LastComponent.size());
|
2017-03-16 23:28:04 +01:00
|
|
|
EXPECT_TRUE(path::is_separator(LastComponent[0], Path.second));
|
2014-03-05 20:56:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-01 00:46:06 +01:00
|
|
|
TEST(Support, HomeDirectory) {
|
2015-10-16 11:40:01 +02:00
|
|
|
std::string expected;
|
2018-04-29 02:45:03 +02:00
|
|
|
#ifdef _WIN32
|
2015-10-16 12:11:07 +02:00
|
|
|
if (wchar_t const *path = ::_wgetenv(L"USERPROFILE")) {
|
|
|
|
auto pathLen = ::wcslen(path);
|
|
|
|
ArrayRef<char> ref{reinterpret_cast<char const *>(path),
|
|
|
|
pathLen * sizeof(wchar_t)};
|
|
|
|
convertUTF16ToUTF8String(ref, expected);
|
|
|
|
}
|
2015-10-16 11:08:59 +02:00
|
|
|
#else
|
2015-10-16 12:11:07 +02:00
|
|
|
if (char const *path = ::getenv("HOME"))
|
|
|
|
expected = path;
|
2014-02-01 00:46:06 +01:00
|
|
|
#endif
|
2015-10-16 12:11:07 +02:00
|
|
|
// Do not try to test it if we don't know what to expect.
|
|
|
|
// On Windows we use something better than env vars.
|
|
|
|
if (!expected.empty()) {
|
2015-10-16 11:40:01 +02:00
|
|
|
SmallString<128> HomeDir;
|
|
|
|
auto status = path::home_directory(HomeDir);
|
2015-10-16 12:11:07 +02:00
|
|
|
EXPECT_TRUE(status);
|
2015-10-16 11:40:01 +02:00
|
|
|
EXPECT_EQ(expected, HomeDir);
|
|
|
|
}
|
2014-02-01 00:46:06 +01:00
|
|
|
}
|
|
|
|
|
2017-03-22 17:30:06 +01:00
|
|
|
#ifdef LLVM_ON_UNIX
|
2017-03-22 16:24:59 +01:00
|
|
|
TEST(Support, HomeDirectoryWithNoEnv) {
|
2017-03-22 17:30:06 +01:00
|
|
|
std::string OriginalStorage;
|
|
|
|
char const *OriginalEnv = ::getenv("HOME");
|
|
|
|
if (OriginalEnv) {
|
|
|
|
// We're going to unset it, so make a copy and save a pointer to the copy
|
|
|
|
// so that we can reset it at the end of the test.
|
|
|
|
OriginalStorage = OriginalEnv;
|
|
|
|
OriginalEnv = OriginalStorage.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't run the test if we have nothing to compare against.
|
|
|
|
struct passwd *pw = getpwuid(getuid());
|
|
|
|
if (!pw || !pw->pw_dir) return;
|
|
|
|
|
2017-03-22 16:24:59 +01:00
|
|
|
::unsetenv("HOME");
|
2017-03-22 17:30:06 +01:00
|
|
|
EXPECT_EQ(nullptr, ::getenv("HOME"));
|
|
|
|
std::string PwDir = pw->pw_dir;
|
2017-03-22 16:24:59 +01:00
|
|
|
|
|
|
|
SmallString<128> HomeDir;
|
|
|
|
auto status = path::home_directory(HomeDir);
|
|
|
|
EXPECT_TRUE(status);
|
2017-03-22 17:30:06 +01:00
|
|
|
EXPECT_EQ(PwDir, HomeDir);
|
2017-03-22 16:24:59 +01:00
|
|
|
|
2017-03-22 17:30:06 +01:00
|
|
|
// Now put the environment back to its original state (meaning that if it was
|
|
|
|
// unset before, we don't reset it).
|
|
|
|
if (OriginalEnv) ::setenv("HOME", OriginalEnv, 1);
|
2017-03-22 16:24:59 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-11-17 17:54:32 +01:00
|
|
|
TEST(Support, TempDirectory) {
|
|
|
|
SmallString<32> TempDir;
|
|
|
|
path::system_temp_directory(false, TempDir);
|
|
|
|
EXPECT_TRUE(!TempDir.empty());
|
|
|
|
TempDir.clear();
|
|
|
|
path::system_temp_directory(true, TempDir);
|
|
|
|
EXPECT_TRUE(!TempDir.empty());
|
|
|
|
}
|
|
|
|
|
2018-04-29 02:45:03 +02:00
|
|
|
#ifdef _WIN32
|
2015-11-17 17:54:32 +01:00
|
|
|
static std::string path2regex(std::string Path) {
|
|
|
|
size_t Pos = 0;
|
|
|
|
while ((Pos = Path.find('\\', Pos)) != std::string::npos) {
|
|
|
|
Path.replace(Pos, 1, "\\\\");
|
|
|
|
Pos += 2;
|
|
|
|
}
|
|
|
|
return Path;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Helper for running temp dir test in separated process. See below.
|
|
|
|
#define EXPECT_TEMP_DIR(prepare, expected) \
|
|
|
|
EXPECT_EXIT( \
|
|
|
|
{ \
|
|
|
|
prepare; \
|
|
|
|
SmallString<300> TempDir; \
|
|
|
|
path::system_temp_directory(true, TempDir); \
|
|
|
|
raw_os_ostream(std::cerr) << TempDir; \
|
|
|
|
std::exit(0); \
|
|
|
|
}, \
|
|
|
|
::testing::ExitedWithCode(0), path2regex(expected))
|
|
|
|
|
|
|
|
TEST(SupportDeathTest, TempDirectoryOnWindows) {
|
|
|
|
// In this test we want to check how system_temp_directory responds to
|
|
|
|
// different values of specific env vars. To prevent corrupting env vars of
|
|
|
|
// the current process all checks are done in separated processes.
|
|
|
|
EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"C:\\OtherFolder"), "C:\\OtherFolder");
|
|
|
|
EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"C:/Unix/Path/Seperators"),
|
|
|
|
"C:\\Unix\\Path\\Seperators");
|
|
|
|
EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"Local Path"), ".+\\Local Path$");
|
|
|
|
EXPECT_TEMP_DIR(_wputenv_s(L"TMP", L"F:\\TrailingSep\\"), "F:\\TrailingSep");
|
|
|
|
EXPECT_TEMP_DIR(
|
|
|
|
_wputenv_s(L"TMP", L"C:\\2\x03C0r-\x00B5\x00B3\\\x2135\x2080"),
|
|
|
|
"C:\\2\xCF\x80r-\xC2\xB5\xC2\xB3\\\xE2\x84\xB5\xE2\x82\x80");
|
|
|
|
|
|
|
|
// Test $TMP empty, $TEMP set.
|
|
|
|
EXPECT_TEMP_DIR(
|
|
|
|
{
|
|
|
|
_wputenv_s(L"TMP", L"");
|
|
|
|
_wputenv_s(L"TEMP", L"C:\\Valid\\Path");
|
|
|
|
},
|
|
|
|
"C:\\Valid\\Path");
|
|
|
|
|
|
|
|
// All related env vars empty
|
|
|
|
EXPECT_TEMP_DIR(
|
|
|
|
{
|
|
|
|
_wputenv_s(L"TMP", L"");
|
|
|
|
_wputenv_s(L"TEMP", L"");
|
|
|
|
_wputenv_s(L"USERPROFILE", L"");
|
|
|
|
},
|
|
|
|
"C:\\Temp");
|
|
|
|
|
|
|
|
// Test evn var / path with 260 chars.
|
|
|
|
SmallString<270> Expected{"C:\\Temp\\AB\\123456789"};
|
|
|
|
while (Expected.size() < 260)
|
|
|
|
Expected.append("\\DirNameWith19Charss");
|
2016-02-10 20:29:01 +01:00
|
|
|
ASSERT_EQ(260U, Expected.size());
|
2015-11-17 17:54:32 +01:00
|
|
|
EXPECT_TEMP_DIR(_putenv_s("TMP", Expected.c_str()), Expected.c_str());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-01-05 17:39:05 +01:00
|
|
|
class FileSystemTest : public testing::Test {
|
|
|
|
protected:
|
|
|
|
/// Unique temporary directory in which all created filesystem entities must
|
2014-11-13 01:12:14 +01:00
|
|
|
/// be placed. It is removed at the end of each test (must be empty).
|
2011-01-05 17:39:05 +01:00
|
|
|
SmallString<128> TestDirectory;
|
2018-06-07 21:58:58 +02:00
|
|
|
SmallString<128> NonExistantFile;
|
2011-01-05 17:39:05 +01:00
|
|
|
|
2015-04-11 04:11:45 +02:00
|
|
|
void SetUp() override {
|
2011-01-05 17:39:05 +01:00
|
|
|
ASSERT_NO_ERROR(
|
2013-06-27 05:45:31 +02:00
|
|
|
fs::createUniqueDirectory("file-system-test", TestDirectory));
|
2011-01-05 17:39:05 +01:00
|
|
|
// We don't care about this specific file.
|
2011-01-05 17:39:46 +01:00
|
|
|
errs() << "Test Directory: " << TestDirectory << '\n';
|
|
|
|
errs().flush();
|
2018-06-07 21:58:58 +02:00
|
|
|
NonExistantFile = TestDirectory;
|
|
|
|
|
|
|
|
// Even though this value is hardcoded, is a 128-bit GUID, so we should be
|
|
|
|
// guaranteed that this file will never exist.
|
|
|
|
sys::path::append(NonExistantFile, "1B28B495C16344CB9822E588CD4C3EF0");
|
2011-01-05 17:39:05 +01:00
|
|
|
}
|
2010-12-03 02:21:28 +01:00
|
|
|
|
2015-04-11 04:11:45 +02:00
|
|
|
void TearDown() override { ASSERT_NO_ERROR(fs::remove(TestDirectory.str())); }
|
2011-01-05 17:39:05 +01:00
|
|
|
};
|
|
|
|
|
2013-06-19 23:03:50 +02:00
|
|
|
TEST_F(FileSystemTest, Unique) {
|
|
|
|
// Create a temp file.
|
|
|
|
int FileDescriptor;
|
|
|
|
SmallString<64> TempPath;
|
|
|
|
ASSERT_NO_ERROR(
|
2013-07-05 22:14:52 +02:00
|
|
|
fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));
|
2013-06-19 23:03:50 +02:00
|
|
|
|
|
|
|
// The same file should return an identical unique id.
|
2013-07-29 23:26:49 +02:00
|
|
|
fs::UniqueID F1, F2;
|
2013-06-20 17:06:35 +02:00
|
|
|
ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), F1));
|
|
|
|
ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), F2));
|
2013-06-19 23:03:50 +02:00
|
|
|
ASSERT_EQ(F1, F2);
|
|
|
|
|
|
|
|
// Different files should return different unique ids.
|
|
|
|
int FileDescriptor2;
|
|
|
|
SmallString<64> TempPath2;
|
|
|
|
ASSERT_NO_ERROR(
|
2013-07-05 22:14:52 +02:00
|
|
|
fs::createTemporaryFile("prefix", "temp", FileDescriptor2, TempPath2));
|
|
|
|
|
2013-07-29 23:26:49 +02:00
|
|
|
fs::UniqueID D;
|
2013-06-20 17:06:35 +02:00
|
|
|
ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath2), D));
|
2013-06-19 23:03:50 +02:00
|
|
|
ASSERT_NE(D, F1);
|
|
|
|
::close(FileDescriptor2);
|
|
|
|
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TempPath2)));
|
|
|
|
|
|
|
|
// Two paths representing the same file on disk should still provide the
|
|
|
|
// same unique id. We can test this by making a hard link.
|
2014-03-11 19:40:24 +01:00
|
|
|
ASSERT_NO_ERROR(fs::create_link(Twine(TempPath), Twine(TempPath2)));
|
2013-07-29 23:26:49 +02:00
|
|
|
fs::UniqueID D2;
|
2013-06-20 17:06:35 +02:00
|
|
|
ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath2), D2));
|
2013-06-19 23:03:50 +02:00
|
|
|
ASSERT_EQ(D2, F1);
|
|
|
|
|
|
|
|
::close(FileDescriptor);
|
2013-07-30 22:25:53 +02:00
|
|
|
|
|
|
|
SmallString<128> Dir1;
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::createUniqueDirectory("dir1", Dir1));
|
|
|
|
ASSERT_NO_ERROR(fs::getUniqueID(Dir1.c_str(), F1));
|
|
|
|
ASSERT_NO_ERROR(fs::getUniqueID(Dir1.c_str(), F2));
|
|
|
|
ASSERT_EQ(F1, F2);
|
|
|
|
|
|
|
|
SmallString<128> Dir2;
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::createUniqueDirectory("dir2", Dir2));
|
|
|
|
ASSERT_NO_ERROR(fs::getUniqueID(Dir2.c_str(), F2));
|
|
|
|
ASSERT_NE(F1, F2);
|
2016-09-02 03:10:53 +02:00
|
|
|
ASSERT_NO_ERROR(fs::remove(Dir1));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Dir2));
|
2016-09-02 02:51:34 +02:00
|
|
|
ASSERT_NO_ERROR(fs::remove(TempPath2));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(TempPath));
|
2013-06-19 23:03:50 +02:00
|
|
|
}
|
|
|
|
|
2017-03-10 18:39:21 +01:00
|
|
|
TEST_F(FileSystemTest, RealPath) {
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_directories(Twine(TestDirectory) + "/test1/test2/test3"));
|
|
|
|
ASSERT_TRUE(fs::exists(Twine(TestDirectory) + "/test1/test2/test3"));
|
|
|
|
|
|
|
|
SmallString<64> RealBase;
|
|
|
|
SmallString<64> Expected;
|
|
|
|
SmallString<64> Actual;
|
|
|
|
|
|
|
|
// TestDirectory itself might be under a symlink or have been specified with
|
|
|
|
// a different case than the existing temp directory. In such cases real_path
|
|
|
|
// on the concatenated path will differ in the TestDirectory portion from
|
|
|
|
// how we specified it. Make sure to compare against the real_path of the
|
|
|
|
// TestDirectory, and not just the value of TestDirectory.
|
|
|
|
ASSERT_NO_ERROR(fs::real_path(TestDirectory, RealBase));
|
|
|
|
path::native(Twine(RealBase) + "/test1/test2", Expected);
|
|
|
|
|
|
|
|
ASSERT_NO_ERROR(fs::real_path(
|
|
|
|
Twine(TestDirectory) + "/././test1/../test1/test2/./test3/..", Actual));
|
|
|
|
|
|
|
|
EXPECT_EQ(Expected, Actual);
|
|
|
|
|
|
|
|
SmallString<64> HomeDir;
|
2018-11-13 19:23:32 +01:00
|
|
|
|
|
|
|
// This can fail if $HOME is not set and getpwuid fails.
|
2017-03-10 18:47:13 +01:00
|
|
|
bool Result = llvm::sys::path::home_directory(HomeDir);
|
|
|
|
if (Result) {
|
|
|
|
ASSERT_NO_ERROR(fs::real_path(HomeDir, Expected));
|
|
|
|
ASSERT_NO_ERROR(fs::real_path("~", Actual, true));
|
|
|
|
EXPECT_EQ(Expected, Actual);
|
|
|
|
ASSERT_NO_ERROR(fs::real_path("~/", Actual, true));
|
|
|
|
EXPECT_EQ(Expected, Actual);
|
|
|
|
}
|
2017-03-10 18:39:21 +01:00
|
|
|
|
|
|
|
ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/test1"));
|
|
|
|
}
|
|
|
|
|
2018-11-13 19:23:32 +01:00
|
|
|
TEST_F(FileSystemTest, ExpandTilde) {
|
|
|
|
SmallString<64> Expected;
|
|
|
|
SmallString<64> Actual;
|
|
|
|
SmallString<64> HomeDir;
|
|
|
|
|
|
|
|
// This can fail if $HOME is not set and getpwuid fails.
|
|
|
|
bool Result = llvm::sys::path::home_directory(HomeDir);
|
|
|
|
if (Result) {
|
|
|
|
fs::expand_tilde(HomeDir, Expected);
|
|
|
|
|
|
|
|
fs::expand_tilde("~", Actual);
|
|
|
|
EXPECT_EQ(Expected, Actual);
|
|
|
|
|
2018-11-14 00:21:00 +01:00
|
|
|
#ifdef _WIN32
|
2018-11-20 22:13:51 +01:00
|
|
|
Expected += "\\foo";
|
2018-11-14 00:21:00 +01:00
|
|
|
fs::expand_tilde("~\\foo", Actual);
|
|
|
|
#else
|
2018-11-20 22:13:51 +01:00
|
|
|
Expected += "/foo";
|
2018-11-13 19:23:32 +01:00
|
|
|
fs::expand_tilde("~/foo", Actual);
|
2018-11-14 00:21:00 +01:00
|
|
|
#endif
|
|
|
|
|
2018-11-13 19:23:32 +01:00
|
|
|
EXPECT_EQ(Expected, Actual);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-09 18:27:45 +01:00
|
|
|
#ifdef LLVM_ON_UNIX
|
|
|
|
TEST_F(FileSystemTest, RealPathNoReadPerm) {
|
|
|
|
SmallString<64> Expanded;
|
|
|
|
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_directories(Twine(TestDirectory) + "/noreadperm"));
|
|
|
|
ASSERT_TRUE(fs::exists(Twine(TestDirectory) + "/noreadperm"));
|
|
|
|
|
|
|
|
fs::setPermissions(Twine(TestDirectory) + "/noreadperm", fs::no_perms);
|
|
|
|
fs::setPermissions(Twine(TestDirectory) + "/noreadperm", fs::all_exe);
|
|
|
|
|
|
|
|
ASSERT_NO_ERROR(fs::real_path(Twine(TestDirectory) + "/noreadperm", Expanded,
|
|
|
|
false));
|
|
|
|
|
|
|
|
ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/noreadperm"));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2017-11-22 20:59:05 +01:00
|
|
|
TEST_F(FileSystemTest, TempFileKeepDiscard) {
|
|
|
|
// We can keep then discard.
|
|
|
|
auto TempFileOrError = fs::TempFile::create(TestDirectory + "/test-%%%%");
|
|
|
|
ASSERT_TRUE((bool)TempFileOrError);
|
|
|
|
fs::TempFile File = std::move(*TempFileOrError);
|
|
|
|
ASSERT_FALSE((bool)File.keep(TestDirectory + "/keep"));
|
|
|
|
ASSERT_FALSE((bool)File.discard());
|
|
|
|
ASSERT_TRUE(fs::exists(TestDirectory + "/keep"));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(TestDirectory + "/keep"));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileSystemTest, TempFileDiscardDiscard) {
|
|
|
|
// We can discard twice.
|
|
|
|
auto TempFileOrError = fs::TempFile::create(TestDirectory + "/test-%%%%");
|
|
|
|
ASSERT_TRUE((bool)TempFileOrError);
|
|
|
|
fs::TempFile File = std::move(*TempFileOrError);
|
|
|
|
ASSERT_FALSE((bool)File.discard());
|
|
|
|
ASSERT_FALSE((bool)File.discard());
|
|
|
|
ASSERT_FALSE(fs::exists(TestDirectory + "/keep"));
|
|
|
|
}
|
|
|
|
|
2011-01-05 17:39:05 +01:00
|
|
|
TEST_F(FileSystemTest, TempFiles) {
|
2010-12-04 04:18:42 +01:00
|
|
|
// Create a temp file.
|
2010-12-03 02:21:28 +01:00
|
|
|
int FileDescriptor;
|
|
|
|
SmallString<64> TempPath;
|
2011-01-04 18:00:18 +01:00
|
|
|
ASSERT_NO_ERROR(
|
2013-07-05 22:14:52 +02:00
|
|
|
fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));
|
2010-12-03 02:21:28 +01:00
|
|
|
|
2010-12-04 04:18:42 +01:00
|
|
|
// Make sure it exists.
|
2014-09-11 21:11:02 +02:00
|
|
|
ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
|
2010-12-03 02:21:28 +01:00
|
|
|
|
2010-12-04 04:18:42 +01:00
|
|
|
// Create another temp tile.
|
|
|
|
int FD2;
|
|
|
|
SmallString<64> TempPath2;
|
2013-07-05 22:14:52 +02:00
|
|
|
ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD2, TempPath2));
|
2013-07-25 17:00:17 +02:00
|
|
|
ASSERT_TRUE(TempPath2.endswith(".temp"));
|
2010-12-04 04:18:42 +01:00
|
|
|
ASSERT_NE(TempPath.str(), TempPath2.str());
|
|
|
|
|
2011-12-12 07:04:28 +01:00
|
|
|
fs::file_status A, B;
|
|
|
|
ASSERT_NO_ERROR(fs::status(Twine(TempPath), A));
|
|
|
|
ASSERT_NO_ERROR(fs::status(Twine(TempPath2), B));
|
|
|
|
EXPECT_FALSE(fs::equivalent(A, B));
|
|
|
|
|
2010-12-04 04:18:42 +01:00
|
|
|
::close(FD2);
|
2013-07-18 05:29:51 +02:00
|
|
|
|
2010-12-04 04:18:42 +01:00
|
|
|
// Remove Temp2.
|
2014-02-23 14:56:14 +01:00
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TempPath2)));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TempPath2)));
|
|
|
|
ASSERT_EQ(fs::remove(Twine(TempPath2), false),
|
2014-06-13 19:20:48 +02:00
|
|
|
errc::no_such_file_or_directory);
|
2010-12-04 04:18:42 +01:00
|
|
|
|
2014-06-13 05:20:08 +02:00
|
|
|
std::error_code EC = fs::status(TempPath2.c_str(), B);
|
2014-06-13 19:20:48 +02:00
|
|
|
EXPECT_EQ(EC, errc::no_such_file_or_directory);
|
2013-07-31 02:10:25 +02:00
|
|
|
EXPECT_EQ(B.type(), fs::file_type::file_not_found);
|
|
|
|
|
2010-12-04 04:18:42 +01:00
|
|
|
// Make sure Temp2 doesn't exist.
|
2014-09-11 22:30:02 +02:00
|
|
|
ASSERT_EQ(fs::access(Twine(TempPath2), sys::fs::AccessMode::Exist),
|
|
|
|
errc::no_such_file_or_directory);
|
2010-12-04 04:18:42 +01:00
|
|
|
|
2013-07-25 17:00:17 +02:00
|
|
|
SmallString<64> TempPath3;
|
|
|
|
ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "", TempPath3));
|
|
|
|
ASSERT_FALSE(TempPath3.endswith("."));
|
2016-09-02 02:51:34 +02:00
|
|
|
FileRemover Cleanup3(TempPath3);
|
2013-07-25 17:00:17 +02:00
|
|
|
|
2010-12-04 04:18:42 +01:00
|
|
|
// Create a hard link to Temp1.
|
2014-03-11 19:40:24 +01:00
|
|
|
ASSERT_NO_ERROR(fs::create_link(Twine(TempPath), Twine(TempPath2)));
|
2010-12-04 04:18:42 +01:00
|
|
|
bool equal;
|
2011-01-04 18:00:18 +01:00
|
|
|
ASSERT_NO_ERROR(fs::equivalent(Twine(TempPath), Twine(TempPath2), equal));
|
2010-12-04 04:18:42 +01:00
|
|
|
EXPECT_TRUE(equal);
|
2011-12-12 07:04:28 +01:00
|
|
|
ASSERT_NO_ERROR(fs::status(Twine(TempPath), A));
|
|
|
|
ASSERT_NO_ERROR(fs::status(Twine(TempPath2), B));
|
|
|
|
EXPECT_TRUE(fs::equivalent(A, B));
|
2010-12-04 04:18:42 +01:00
|
|
|
|
|
|
|
// Remove Temp1.
|
2010-12-03 02:21:28 +01:00
|
|
|
::close(FileDescriptor);
|
2014-02-23 14:56:14 +01:00
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TempPath)));
|
2010-12-03 02:21:28 +01:00
|
|
|
|
2010-12-04 04:18:42 +01:00
|
|
|
// Remove the hard link.
|
2014-02-23 14:56:14 +01:00
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TempPath2)));
|
2010-12-04 04:18:42 +01:00
|
|
|
|
|
|
|
// Make sure Temp1 doesn't exist.
|
2014-09-11 22:30:02 +02:00
|
|
|
ASSERT_EQ(fs::access(Twine(TempPath), sys::fs::AccessMode::Exist),
|
|
|
|
errc::no_such_file_or_directory);
|
2013-03-16 16:00:51 +01:00
|
|
|
|
2018-04-29 02:45:03 +02:00
|
|
|
#ifdef _WIN32
|
2013-03-16 16:00:51 +01:00
|
|
|
// Path name > 260 chars should get an error.
|
|
|
|
const char *Path270 =
|
|
|
|
"abcdefghijklmnopqrstuvwxyz9abcdefghijklmnopqrstuvwxyz8"
|
|
|
|
"abcdefghijklmnopqrstuvwxyz7abcdefghijklmnopqrstuvwxyz6"
|
|
|
|
"abcdefghijklmnopqrstuvwxyz5abcdefghijklmnopqrstuvwxyz4"
|
|
|
|
"abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz2"
|
|
|
|
"abcdefghijklmnopqrstuvwxyz1abcdefghijklmnopqrstuvwxyz0";
|
2014-11-13 01:36:34 +01:00
|
|
|
EXPECT_EQ(fs::createUniqueFile(Path270, FileDescriptor, TempPath),
|
2014-11-13 01:12:14 +01:00
|
|
|
errc::invalid_argument);
|
|
|
|
// Relative path < 247 chars, no problem.
|
|
|
|
const char *Path216 =
|
|
|
|
"abcdefghijklmnopqrstuvwxyz7abcdefghijklmnopqrstuvwxyz6"
|
|
|
|
"abcdefghijklmnopqrstuvwxyz5abcdefghijklmnopqrstuvwxyz4"
|
|
|
|
"abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz2"
|
|
|
|
"abcdefghijklmnopqrstuvwxyz1abcdefghijklmnopqrstuvwxyz0";
|
2014-11-13 01:36:34 +01:00
|
|
|
ASSERT_NO_ERROR(fs::createTemporaryFile(Path216, "", TempPath));
|
2014-11-13 01:12:14 +01:00
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TempPath)));
|
2013-03-16 16:00:51 +01:00
|
|
|
#endif
|
2011-01-05 17:39:05 +01:00
|
|
|
}
|
2010-12-06 05:28:42 +01:00
|
|
|
|
2018-08-02 19:41:38 +02:00
|
|
|
TEST_F(FileSystemTest, TempFileCollisions) {
|
|
|
|
SmallString<128> TestDirectory;
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::createUniqueDirectory("CreateUniqueFileTest", TestDirectory));
|
|
|
|
FileRemover Cleanup(TestDirectory);
|
|
|
|
SmallString<128> Model = TestDirectory;
|
|
|
|
path::append(Model, "%.tmp");
|
|
|
|
SmallString<128> Path;
|
|
|
|
std::vector<fs::TempFile> TempFiles;
|
|
|
|
|
|
|
|
auto TryCreateTempFile = [&]() {
|
|
|
|
Expected<fs::TempFile> T = fs::TempFile::create(Model);
|
|
|
|
if (T) {
|
|
|
|
TempFiles.push_back(std::move(*T));
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
logAllUnhandledErrors(T.takeError(), errs(),
|
|
|
|
"Failed to create temporary file: ");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// We should be able to create exactly 16 temporary files.
|
|
|
|
for (int i = 0; i < 16; ++i)
|
|
|
|
EXPECT_TRUE(TryCreateTempFile());
|
|
|
|
EXPECT_FALSE(TryCreateTempFile());
|
|
|
|
|
|
|
|
for (fs::TempFile &T : TempFiles)
|
|
|
|
cantFail(T.discard());
|
|
|
|
}
|
|
|
|
|
2014-02-23 14:56:14 +01:00
|
|
|
TEST_F(FileSystemTest, CreateDir) {
|
|
|
|
ASSERT_NO_ERROR(fs::create_directory(Twine(TestDirectory) + "foo"));
|
|
|
|
ASSERT_NO_ERROR(fs::create_directory(Twine(TestDirectory) + "foo"));
|
|
|
|
ASSERT_EQ(fs::create_directory(Twine(TestDirectory) + "foo", false),
|
2014-06-13 19:20:48 +02:00
|
|
|
errc::file_exists);
|
2014-02-23 14:56:14 +01:00
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "foo"));
|
2014-11-13 01:12:14 +01:00
|
|
|
|
2015-08-06 23:04:55 +02:00
|
|
|
#ifdef LLVM_ON_UNIX
|
|
|
|
// Set a 0000 umask so that we can test our directory permissions.
|
|
|
|
mode_t OldUmask = ::umask(0000);
|
|
|
|
|
|
|
|
fs::file_status Status;
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_directory(Twine(TestDirectory) + "baz500", false,
|
|
|
|
fs::perms::owner_read | fs::perms::owner_exe));
|
|
|
|
ASSERT_NO_ERROR(fs::status(Twine(TestDirectory) + "baz500", Status));
|
|
|
|
ASSERT_EQ(Status.permissions() & fs::perms::all_all,
|
|
|
|
fs::perms::owner_read | fs::perms::owner_exe);
|
|
|
|
ASSERT_NO_ERROR(fs::create_directory(Twine(TestDirectory) + "baz777", false,
|
|
|
|
fs::perms::all_all));
|
|
|
|
ASSERT_NO_ERROR(fs::status(Twine(TestDirectory) + "baz777", Status));
|
|
|
|
ASSERT_EQ(Status.permissions() & fs::perms::all_all, fs::perms::all_all);
|
|
|
|
|
|
|
|
// Restore umask to be safe.
|
|
|
|
::umask(OldUmask);
|
|
|
|
#endif
|
|
|
|
|
2018-04-29 02:45:03 +02:00
|
|
|
#ifdef _WIN32
|
2014-11-13 01:12:14 +01:00
|
|
|
// Prove that create_directories() can handle a pathname > 248 characters,
|
|
|
|
// which is the documented limit for CreateDirectory().
|
|
|
|
// (248 is MAX_PATH subtracting room for an 8.3 filename.)
|
|
|
|
// Generate a directory path guaranteed to fall into that range.
|
|
|
|
size_t TmpLen = TestDirectory.size();
|
|
|
|
const char *OneDir = "\\123456789";
|
|
|
|
size_t OneDirLen = strlen(OneDir);
|
2014-11-13 14:39:49 +01:00
|
|
|
ASSERT_LT(OneDirLen, 12U);
|
2014-11-13 01:12:14 +01:00
|
|
|
size_t NLevels = ((248 - TmpLen) / OneDirLen) + 1;
|
|
|
|
SmallString<260> LongDir(TestDirectory);
|
|
|
|
for (size_t I = 0; I < NLevels; ++I)
|
|
|
|
LongDir.append(OneDir);
|
|
|
|
ASSERT_NO_ERROR(fs::create_directories(Twine(LongDir)));
|
|
|
|
ASSERT_NO_ERROR(fs::create_directories(Twine(LongDir)));
|
|
|
|
ASSERT_EQ(fs::create_directories(Twine(LongDir), false),
|
|
|
|
errc::file_exists);
|
|
|
|
// Tidy up, "recursively" removing the directories.
|
|
|
|
StringRef ThisDir(LongDir);
|
|
|
|
for (size_t J = 0; J < NLevels; ++J) {
|
|
|
|
ASSERT_NO_ERROR(fs::remove(ThisDir));
|
|
|
|
ThisDir = path::parent_path(ThisDir);
|
|
|
|
}
|
|
|
|
|
2017-08-21 22:49:44 +02:00
|
|
|
// Also verify that paths with Unix separators are handled correctly.
|
|
|
|
std::string LongPathWithUnixSeparators(TestDirectory.str());
|
|
|
|
// Add at least one subdirectory to TestDirectory, and replace slashes with
|
|
|
|
// backslashes
|
|
|
|
do {
|
|
|
|
LongPathWithUnixSeparators.append("/DirNameWith19Charss");
|
|
|
|
} while (LongPathWithUnixSeparators.size() < 260);
|
|
|
|
std::replace(LongPathWithUnixSeparators.begin(),
|
|
|
|
LongPathWithUnixSeparators.end(),
|
|
|
|
'\\', '/');
|
|
|
|
ASSERT_NO_ERROR(fs::create_directories(Twine(LongPathWithUnixSeparators)));
|
|
|
|
// cleanup
|
|
|
|
ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) +
|
|
|
|
"/DirNameWith19Charss"));
|
|
|
|
|
2014-11-13 01:12:14 +01:00
|
|
|
// Similarly for a relative pathname. Need to set the current directory to
|
|
|
|
// TestDirectory so that the one we create ends up in the right place.
|
|
|
|
char PreviousDir[260];
|
|
|
|
size_t PreviousDirLen = ::GetCurrentDirectoryA(260, PreviousDir);
|
2014-11-13 14:39:49 +01:00
|
|
|
ASSERT_GT(PreviousDirLen, 0U);
|
|
|
|
ASSERT_LT(PreviousDirLen, 260U);
|
2014-11-13 01:12:14 +01:00
|
|
|
ASSERT_NE(::SetCurrentDirectoryA(TestDirectory.c_str()), 0);
|
|
|
|
LongDir.clear();
|
|
|
|
// Generate a relative directory name with absolute length > 248.
|
|
|
|
size_t LongDirLen = 249 - TestDirectory.size();
|
|
|
|
LongDir.assign(LongDirLen, 'a');
|
|
|
|
ASSERT_NO_ERROR(fs::create_directory(Twine(LongDir)));
|
|
|
|
// While we're here, prove that .. and . handling works in these long paths.
|
|
|
|
const char *DotDotDirs = "\\..\\.\\b";
|
|
|
|
LongDir.append(DotDotDirs);
|
2014-11-13 01:36:34 +01:00
|
|
|
ASSERT_NO_ERROR(fs::create_directory("b"));
|
2014-11-13 01:12:14 +01:00
|
|
|
ASSERT_EQ(fs::create_directory(Twine(LongDir), false), errc::file_exists);
|
|
|
|
// And clean up.
|
2014-11-13 01:36:34 +01:00
|
|
|
ASSERT_NO_ERROR(fs::remove("b"));
|
2014-11-13 01:12:14 +01:00
|
|
|
ASSERT_NO_ERROR(fs::remove(
|
|
|
|
Twine(LongDir.substr(0, LongDir.size() - strlen(DotDotDirs)))));
|
|
|
|
ASSERT_NE(::SetCurrentDirectoryA(PreviousDir), 0);
|
|
|
|
#endif
|
2014-02-23 14:56:14 +01:00
|
|
|
}
|
|
|
|
|
2011-01-05 17:39:05 +01:00
|
|
|
TEST_F(FileSystemTest, DirectoryIteration) {
|
2014-06-13 05:20:08 +02:00
|
|
|
std::error_code ec;
|
2011-01-06 06:57:54 +01:00
|
|
|
for (fs::directory_iterator i(".", ec), e; i != e; i.increment(ec))
|
|
|
|
ASSERT_NO_ERROR(ec);
|
2011-12-08 23:50:09 +01:00
|
|
|
|
|
|
|
// Create a known hierarchy to recurse over.
|
2014-02-23 14:56:14 +01:00
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_directories(Twine(TestDirectory) + "/recursive/a0/aa1"));
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_directories(Twine(TestDirectory) + "/recursive/a0/ab1"));
|
|
|
|
ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) +
|
|
|
|
"/recursive/dontlookhere/da1"));
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_directories(Twine(TestDirectory) + "/recursive/z0/za1"));
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_directories(Twine(TestDirectory) + "/recursive/pop/p1"));
|
2011-12-08 23:50:09 +01:00
|
|
|
typedef std::vector<std::string> v_t;
|
|
|
|
v_t visited;
|
|
|
|
for (fs::recursive_directory_iterator i(Twine(TestDirectory)
|
|
|
|
+ "/recursive", ec), e; i != e; i.increment(ec)){
|
|
|
|
ASSERT_NO_ERROR(ec);
|
2011-12-10 00:20:03 +01:00
|
|
|
if (path::filename(i->path()) == "p1") {
|
2011-12-08 23:50:09 +01:00
|
|
|
i.pop();
|
2011-12-10 00:20:03 +01:00
|
|
|
// FIXME: recursive_directory_iterator should be more robust.
|
|
|
|
if (i == e) break;
|
|
|
|
}
|
2011-12-09 02:14:41 +01:00
|
|
|
if (path::filename(i->path()) == "dontlookhere")
|
|
|
|
i.no_push();
|
2011-12-08 23:50:09 +01:00
|
|
|
visited.push_back(path::filename(i->path()));
|
|
|
|
}
|
2016-08-12 00:21:41 +02:00
|
|
|
v_t::const_iterator a0 = find(visited, "a0");
|
|
|
|
v_t::const_iterator aa1 = find(visited, "aa1");
|
|
|
|
v_t::const_iterator ab1 = find(visited, "ab1");
|
|
|
|
v_t::const_iterator dontlookhere = find(visited, "dontlookhere");
|
|
|
|
v_t::const_iterator da1 = find(visited, "da1");
|
|
|
|
v_t::const_iterator z0 = find(visited, "z0");
|
|
|
|
v_t::const_iterator za1 = find(visited, "za1");
|
|
|
|
v_t::const_iterator pop = find(visited, "pop");
|
|
|
|
v_t::const_iterator p1 = find(visited, "p1");
|
2011-12-08 23:50:09 +01:00
|
|
|
|
|
|
|
// Make sure that each path was visited correctly.
|
|
|
|
ASSERT_NE(a0, visited.end());
|
|
|
|
ASSERT_NE(aa1, visited.end());
|
|
|
|
ASSERT_NE(ab1, visited.end());
|
|
|
|
ASSERT_NE(dontlookhere, visited.end());
|
|
|
|
ASSERT_EQ(da1, visited.end()); // Not visited.
|
|
|
|
ASSERT_NE(z0, visited.end());
|
|
|
|
ASSERT_NE(za1, visited.end());
|
|
|
|
ASSERT_NE(pop, visited.end());
|
|
|
|
ASSERT_EQ(p1, visited.end()); // Not visited.
|
|
|
|
|
|
|
|
// Make sure that parents were visited before children. No other ordering
|
|
|
|
// guarantees can be made across siblings.
|
|
|
|
ASSERT_LT(a0, aa1);
|
|
|
|
ASSERT_LT(a0, ab1);
|
|
|
|
ASSERT_LT(z0, za1);
|
2014-01-10 21:36:42 +01:00
|
|
|
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/a0/aa1"));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/a0/ab1"));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/a0"));
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::remove(Twine(TestDirectory) + "/recursive/dontlookhere/da1"));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/dontlookhere"));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/pop/p1"));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/pop"));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/z0/za1"));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive/z0"));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/recursive"));
|
2016-05-13 23:31:32 +02:00
|
|
|
|
|
|
|
// Test recursive_directory_iterator level()
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_directories(Twine(TestDirectory) + "/reclevel/a/b/c"));
|
|
|
|
fs::recursive_directory_iterator I(Twine(TestDirectory) + "/reclevel", ec), E;
|
|
|
|
for (int l = 0; I != E; I.increment(ec), ++l) {
|
|
|
|
ASSERT_NO_ERROR(ec);
|
|
|
|
EXPECT_EQ(I.level(), l);
|
|
|
|
}
|
|
|
|
EXPECT_EQ(I, E);
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a/b/c"));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a/b"));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel/a"));
|
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory) + "/reclevel"));
|
2011-01-06 06:57:54 +01:00
|
|
|
}
|
2011-01-06 06:58:02 +01:00
|
|
|
|
2017-03-13 22:34:07 +01:00
|
|
|
#ifdef LLVM_ON_UNIX
|
|
|
|
TEST_F(FileSystemTest, BrokenSymlinkDirectoryIteration) {
|
|
|
|
// Create a known hierarchy to recurse over.
|
|
|
|
ASSERT_NO_ERROR(fs::create_directories(Twine(TestDirectory) + "/symlink"));
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/a"));
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_directories(Twine(TestDirectory) + "/symlink/b/bb"));
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/b/ba"));
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/b/bc"));
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/c"));
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_directories(Twine(TestDirectory) + "/symlink/d/dd/ddd"));
|
|
|
|
ASSERT_NO_ERROR(fs::create_link(Twine(TestDirectory) + "/symlink/d/dd",
|
|
|
|
Twine(TestDirectory) + "/symlink/d/da"));
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::create_link("no_such_file", Twine(TestDirectory) + "/symlink/e"));
|
|
|
|
|
|
|
|
typedef std::vector<std::string> v_t;
|
2018-04-05 21:43:24 +02:00
|
|
|
v_t VisitedNonBrokenSymlinks;
|
|
|
|
v_t VisitedBrokenSymlinks;
|
2017-03-13 22:34:07 +01:00
|
|
|
std::error_code ec;
|
2018-10-01 14:17:05 +02:00
|
|
|
using testing::UnorderedElementsAre;
|
|
|
|
using testing::UnorderedElementsAreArray;
|
2018-04-05 21:43:24 +02:00
|
|
|
|
|
|
|
// Broken symbol links are expected to throw an error.
|
2017-03-13 22:34:07 +01:00
|
|
|
for (fs::directory_iterator i(Twine(TestDirectory) + "/symlink", ec), e;
|
|
|
|
i != e; i.increment(ec)) {
|
2018-10-01 14:17:05 +02:00
|
|
|
ASSERT_NO_ERROR(ec);
|
|
|
|
if (i->status().getError() ==
|
|
|
|
std::make_error_code(std::errc::no_such_file_or_directory)) {
|
2018-04-05 21:43:24 +02:00
|
|
|
VisitedBrokenSymlinks.push_back(path::filename(i->path()));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
VisitedNonBrokenSymlinks.push_back(path::filename(i->path()));
|
2017-03-13 22:34:07 +01:00
|
|
|
}
|
2018-10-01 14:17:05 +02:00
|
|
|
EXPECT_THAT(VisitedNonBrokenSymlinks, UnorderedElementsAre("b", "d"));
|
2018-04-05 21:43:24 +02:00
|
|
|
VisitedNonBrokenSymlinks.clear();
|
|
|
|
|
2018-10-01 14:17:05 +02:00
|
|
|
EXPECT_THAT(VisitedBrokenSymlinks, UnorderedElementsAre("a", "c", "e"));
|
2018-04-05 21:43:24 +02:00
|
|
|
VisitedBrokenSymlinks.clear();
|
|
|
|
|
|
|
|
// Broken symbol links are expected to throw an error.
|
|
|
|
for (fs::recursive_directory_iterator i(
|
|
|
|
Twine(TestDirectory) + "/symlink", ec), e; i != e; i.increment(ec)) {
|
2018-10-01 14:17:05 +02:00
|
|
|
ASSERT_NO_ERROR(ec);
|
|
|
|
if (i->status().getError() ==
|
|
|
|
std::make_error_code(std::errc::no_such_file_or_directory)) {
|
2018-04-05 21:43:24 +02:00
|
|
|
VisitedBrokenSymlinks.push_back(path::filename(i->path()));
|
2017-03-13 22:34:07 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-04-05 21:43:24 +02:00
|
|
|
VisitedNonBrokenSymlinks.push_back(path::filename(i->path()));
|
2017-03-13 22:34:07 +01:00
|
|
|
}
|
2018-10-01 14:17:05 +02:00
|
|
|
EXPECT_THAT(VisitedNonBrokenSymlinks,
|
|
|
|
UnorderedElementsAre("b", "bb", "d", "da", "dd", "ddd", "ddd"));
|
2018-04-05 21:43:24 +02:00
|
|
|
VisitedNonBrokenSymlinks.clear();
|
|
|
|
|
2018-10-01 14:17:05 +02:00
|
|
|
EXPECT_THAT(VisitedBrokenSymlinks,
|
|
|
|
UnorderedElementsAre("a", "ba", "bc", "c", "e"));
|
2018-04-05 21:43:24 +02:00
|
|
|
VisitedBrokenSymlinks.clear();
|
|
|
|
|
|
|
|
for (fs::recursive_directory_iterator i(
|
|
|
|
Twine(TestDirectory) + "/symlink", ec, /*follow_symlinks=*/false), e;
|
2017-03-13 22:34:07 +01:00
|
|
|
i != e; i.increment(ec)) {
|
2018-10-01 14:17:05 +02:00
|
|
|
ASSERT_NO_ERROR(ec);
|
|
|
|
if (i->status().getError() ==
|
|
|
|
std::make_error_code(std::errc::no_such_file_or_directory)) {
|
2018-04-05 21:43:24 +02:00
|
|
|
VisitedBrokenSymlinks.push_back(path::filename(i->path()));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
VisitedNonBrokenSymlinks.push_back(path::filename(i->path()));
|
2017-03-13 22:34:07 +01:00
|
|
|
}
|
2018-10-01 14:17:05 +02:00
|
|
|
EXPECT_THAT(VisitedNonBrokenSymlinks,
|
|
|
|
UnorderedElementsAreArray({"a", "b", "ba", "bb", "bc", "c", "d",
|
|
|
|
"da", "dd", "ddd", "e"}));
|
2018-04-05 21:43:24 +02:00
|
|
|
VisitedNonBrokenSymlinks.clear();
|
|
|
|
|
2018-10-01 14:17:05 +02:00
|
|
|
EXPECT_THAT(VisitedBrokenSymlinks, UnorderedElementsAre());
|
2018-04-05 21:43:24 +02:00
|
|
|
VisitedBrokenSymlinks.clear();
|
2017-03-13 22:34:07 +01:00
|
|
|
|
|
|
|
ASSERT_NO_ERROR(fs::remove_directories(Twine(TestDirectory) + "/symlink"));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-03-08 23:49:32 +01:00
|
|
|
TEST_F(FileSystemTest, Remove) {
|
|
|
|
SmallString<64> BaseDir;
|
|
|
|
SmallString<64> Paths[4];
|
|
|
|
int fds[4];
|
|
|
|
ASSERT_NO_ERROR(fs::createUniqueDirectory("fs_remove", BaseDir));
|
|
|
|
|
|
|
|
ASSERT_NO_ERROR(fs::create_directories(Twine(BaseDir) + "/foo/bar/baz"));
|
|
|
|
ASSERT_NO_ERROR(fs::create_directories(Twine(BaseDir) + "/foo/bar/buzz"));
|
|
|
|
ASSERT_NO_ERROR(fs::createUniqueFile(
|
|
|
|
Twine(BaseDir) + "/foo/bar/baz/%%%%%%.tmp", fds[0], Paths[0]));
|
|
|
|
ASSERT_NO_ERROR(fs::createUniqueFile(
|
|
|
|
Twine(BaseDir) + "/foo/bar/baz/%%%%%%.tmp", fds[1], Paths[1]));
|
|
|
|
ASSERT_NO_ERROR(fs::createUniqueFile(
|
|
|
|
Twine(BaseDir) + "/foo/bar/buzz/%%%%%%.tmp", fds[2], Paths[2]));
|
|
|
|
ASSERT_NO_ERROR(fs::createUniqueFile(
|
|
|
|
Twine(BaseDir) + "/foo/bar/buzz/%%%%%%.tmp", fds[3], Paths[3]));
|
|
|
|
|
|
|
|
for (int fd : fds)
|
|
|
|
::close(fd);
|
|
|
|
|
|
|
|
EXPECT_TRUE(fs::exists(Twine(BaseDir) + "/foo/bar/baz"));
|
|
|
|
EXPECT_TRUE(fs::exists(Twine(BaseDir) + "/foo/bar/buzz"));
|
|
|
|
EXPECT_TRUE(fs::exists(Paths[0]));
|
|
|
|
EXPECT_TRUE(fs::exists(Paths[1]));
|
|
|
|
EXPECT_TRUE(fs::exists(Paths[2]));
|
|
|
|
EXPECT_TRUE(fs::exists(Paths[3]));
|
|
|
|
|
|
|
|
ASSERT_NO_ERROR(fs::remove_directories("D:/footest"));
|
|
|
|
|
|
|
|
ASSERT_NO_ERROR(fs::remove_directories(BaseDir));
|
|
|
|
ASSERT_FALSE(fs::exists(BaseDir));
|
|
|
|
}
|
|
|
|
|
2018-04-29 02:45:03 +02:00
|
|
|
#ifdef _WIN32
|
2013-07-19 16:41:25 +02:00
|
|
|
TEST_F(FileSystemTest, CarriageReturn) {
|
|
|
|
SmallString<128> FilePathname(TestDirectory);
|
2014-08-26 02:24:23 +02:00
|
|
|
std::error_code EC;
|
2013-07-19 16:41:25 +02:00
|
|
|
path::append(FilePathname, "test");
|
|
|
|
|
|
|
|
{
|
2014-08-26 02:24:23 +02:00
|
|
|
raw_fd_ostream File(FilePathname, EC, sys::fs::F_Text);
|
|
|
|
ASSERT_NO_ERROR(EC);
|
2013-07-19 16:41:25 +02:00
|
|
|
File << '\n';
|
|
|
|
}
|
|
|
|
{
|
2014-08-26 02:24:23 +02:00
|
|
|
auto Buf = MemoryBuffer::getFile(FilePathname.str());
|
2014-07-06 22:20:02 +02:00
|
|
|
EXPECT_TRUE((bool)Buf);
|
2014-07-06 21:34:52 +02:00
|
|
|
EXPECT_EQ(Buf.get()->getBuffer(), "\r\n");
|
2013-07-19 16:41:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2014-08-26 02:24:23 +02:00
|
|
|
raw_fd_ostream File(FilePathname, EC, sys::fs::F_None);
|
|
|
|
ASSERT_NO_ERROR(EC);
|
2013-07-19 16:41:25 +02:00
|
|
|
File << '\n';
|
|
|
|
}
|
|
|
|
{
|
2014-08-26 02:24:23 +02:00
|
|
|
auto Buf = MemoryBuffer::getFile(FilePathname.str());
|
2014-07-06 22:20:02 +02:00
|
|
|
EXPECT_TRUE((bool)Buf);
|
2014-07-06 21:34:52 +02:00
|
|
|
EXPECT_EQ(Buf.get()->getBuffer(), "\n");
|
2013-07-19 16:41:25 +02:00
|
|
|
}
|
2014-01-10 21:36:42 +01:00
|
|
|
ASSERT_NO_ERROR(fs::remove(Twine(FilePathname)));
|
2013-07-19 16:41:25 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-12-12 18:55:12 +01:00
|
|
|
TEST_F(FileSystemTest, Resize) {
|
|
|
|
int FD;
|
|
|
|
SmallString<64> TempPath;
|
|
|
|
ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));
|
|
|
|
ASSERT_NO_ERROR(fs::resize_file(FD, 123));
|
|
|
|
fs::file_status Status;
|
|
|
|
ASSERT_NO_ERROR(fs::status(FD, Status));
|
|
|
|
ASSERT_EQ(Status.getSize(), 123U);
|
2016-09-02 02:51:34 +02:00
|
|
|
::close(FD);
|
|
|
|
ASSERT_NO_ERROR(fs::remove(TempPath));
|
2014-12-12 18:55:12 +01:00
|
|
|
}
|
|
|
|
|
2017-03-21 00:33:18 +01:00
|
|
|
TEST_F(FileSystemTest, MD5) {
|
|
|
|
int FD;
|
|
|
|
SmallString<64> TempPath;
|
|
|
|
ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));
|
|
|
|
StringRef Data("abcdefghijklmnopqrstuvwxyz");
|
2017-05-12 19:00:13 +02:00
|
|
|
ASSERT_EQ(write(FD, Data.data(), Data.size()), static_cast<ssize_t>(Data.size()));
|
2017-03-21 00:33:18 +01:00
|
|
|
lseek(FD, 0, SEEK_SET);
|
|
|
|
auto Hash = fs::md5_contents(FD);
|
|
|
|
::close(FD);
|
|
|
|
ASSERT_NO_ERROR(Hash.getError());
|
|
|
|
|
|
|
|
EXPECT_STREQ("c3fcd3d76192e4007dfb496cca67e13b", Hash->digest().c_str());
|
|
|
|
}
|
|
|
|
|
2012-06-20 02:28:54 +02:00
|
|
|
TEST_F(FileSystemTest, FileMapping) {
|
|
|
|
// Create a temp file.
|
|
|
|
int FileDescriptor;
|
|
|
|
SmallString<64> TempPath;
|
|
|
|
ASSERT_NO_ERROR(
|
2013-07-05 22:14:52 +02:00
|
|
|
fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));
|
2014-12-12 19:13:23 +01:00
|
|
|
unsigned Size = 4096;
|
|
|
|
ASSERT_NO_ERROR(fs::resize_file(FileDescriptor, Size));
|
|
|
|
|
2012-06-20 02:28:54 +02:00
|
|
|
// Map in temp file and add some content
|
2014-06-13 05:20:08 +02:00
|
|
|
std::error_code EC;
|
2012-08-15 21:05:47 +02:00
|
|
|
StringRef Val("hello there");
|
|
|
|
{
|
|
|
|
fs::mapped_file_region mfr(FileDescriptor,
|
2014-12-12 19:13:23 +01:00
|
|
|
fs::mapped_file_region::readwrite, Size, 0, EC);
|
2012-08-15 21:05:47 +02:00
|
|
|
ASSERT_NO_ERROR(EC);
|
|
|
|
std::copy(Val.begin(), Val.end(), mfr.data());
|
|
|
|
// Explicitly add a 0.
|
|
|
|
mfr.data()[Val.size()] = 0;
|
|
|
|
// Unmap temp file
|
|
|
|
}
|
2016-09-02 02:51:34 +02:00
|
|
|
ASSERT_EQ(close(FileDescriptor), 0);
|
2013-11-13 21:31:21 +01:00
|
|
|
|
2012-06-20 02:28:54 +02:00
|
|
|
// Map it back in read-only
|
2016-09-02 02:51:34 +02:00
|
|
|
{
|
|
|
|
int FD;
|
|
|
|
EC = fs::openFileForRead(Twine(TempPath), FD);
|
|
|
|
ASSERT_NO_ERROR(EC);
|
|
|
|
fs::mapped_file_region mfr(FD, fs::mapped_file_region::readonly, Size, 0, EC);
|
|
|
|
ASSERT_NO_ERROR(EC);
|
|
|
|
|
|
|
|
// Verify content
|
|
|
|
EXPECT_EQ(StringRef(mfr.const_data()), Val);
|
|
|
|
|
|
|
|
// Unmap temp file
|
|
|
|
fs::mapped_file_region m(FD, fs::mapped_file_region::readonly, Size, 0, EC);
|
|
|
|
ASSERT_NO_ERROR(EC);
|
|
|
|
ASSERT_EQ(close(FD), 0);
|
|
|
|
}
|
|
|
|
ASSERT_NO_ERROR(fs::remove(TempPath));
|
2012-08-15 21:05:47 +02:00
|
|
|
}
|
2014-03-11 23:05:42 +01:00
|
|
|
|
|
|
|
TEST(Support, NormalizePath) {
|
2017-03-17 01:16:21 +01:00
|
|
|
using TestTuple = std::tuple<const char *, const char *, const char *>;
|
|
|
|
std::vector<TestTuple> Tests;
|
|
|
|
Tests.emplace_back("a", "a", "a");
|
|
|
|
Tests.emplace_back("a/b", "a\\b", "a/b");
|
|
|
|
Tests.emplace_back("a\\b", "a\\b", "a/b");
|
|
|
|
Tests.emplace_back("a\\\\b", "a\\\\b", "a\\\\b");
|
|
|
|
Tests.emplace_back("\\a", "\\a", "/a");
|
|
|
|
Tests.emplace_back("a\\", "a\\", "a/");
|
|
|
|
|
2017-03-16 23:28:04 +01:00
|
|
|
for (auto &T : Tests) {
|
2017-03-17 01:28:23 +01:00
|
|
|
SmallString<64> Win(std::get<0>(T));
|
|
|
|
SmallString<64> Posix(Win);
|
2017-03-16 23:28:04 +01:00
|
|
|
path::native(Win, path::Style::windows);
|
|
|
|
path::native(Posix, path::Style::posix);
|
|
|
|
EXPECT_EQ(std::get<1>(T), Win);
|
|
|
|
EXPECT_EQ(std::get<2>(T), Posix);
|
|
|
|
}
|
2017-03-01 10:38:15 +01:00
|
|
|
|
2018-04-29 02:45:03 +02:00
|
|
|
#if defined(_WIN32)
|
2017-03-01 10:38:15 +01:00
|
|
|
SmallString<64> PathHome;
|
|
|
|
path::home_directory(PathHome);
|
|
|
|
|
|
|
|
const char *Path7a = "~/aaa";
|
|
|
|
SmallString<64> Path7(Path7a);
|
|
|
|
path::native(Path7);
|
|
|
|
EXPECT_TRUE(Path7.endswith("\\aaa"));
|
|
|
|
EXPECT_TRUE(Path7.startswith(PathHome));
|
|
|
|
EXPECT_EQ(Path7.size(), PathHome.size() + strlen(Path7a + 1));
|
|
|
|
|
|
|
|
const char *Path8a = "~";
|
|
|
|
SmallString<64> Path8(Path8a);
|
|
|
|
path::native(Path8);
|
|
|
|
EXPECT_EQ(Path8, PathHome);
|
|
|
|
|
|
|
|
const char *Path9a = "~aaa";
|
|
|
|
SmallString<64> Path9(Path9a);
|
|
|
|
path::native(Path9);
|
|
|
|
EXPECT_EQ(Path9, "~aaa");
|
|
|
|
|
|
|
|
const char *Path10a = "aaa/~/b";
|
|
|
|
SmallString<64> Path10(Path10a);
|
|
|
|
path::native(Path10);
|
|
|
|
EXPECT_EQ(Path10, "aaa\\~\\b");
|
|
|
|
#endif
|
2014-03-11 23:05:42 +01:00
|
|
|
}
|
2015-09-02 23:02:10 +02:00
|
|
|
|
|
|
|
TEST(Support, RemoveLeadingDotSlash) {
|
|
|
|
StringRef Path1("././/foolz/wat");
|
|
|
|
StringRef Path2("./////");
|
|
|
|
|
|
|
|
Path1 = path::remove_leading_dotslash(Path1);
|
|
|
|
EXPECT_EQ(Path1, "foolz/wat");
|
|
|
|
Path2 = path::remove_leading_dotslash(Path2);
|
|
|
|
EXPECT_EQ(Path2, "");
|
|
|
|
}
|
2015-11-09 19:56:31 +01:00
|
|
|
|
2017-03-16 23:28:04 +01:00
|
|
|
static std::string remove_dots(StringRef path, bool remove_dot_dot,
|
|
|
|
path::Style style) {
|
2015-11-09 19:56:31 +01:00
|
|
|
SmallString<256> buffer(path);
|
2017-03-16 23:28:04 +01:00
|
|
|
path::remove_dots(buffer, remove_dot_dot, style);
|
2015-11-09 19:56:31 +01:00
|
|
|
return buffer.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Support, RemoveDots) {
|
2017-03-16 23:28:04 +01:00
|
|
|
EXPECT_EQ("foolz\\wat",
|
|
|
|
remove_dots(".\\.\\\\foolz\\wat", false, path::Style::windows));
|
2018-05-17 01:29:36 +02:00
|
|
|
EXPECT_EQ("", remove_dots(".\\\\\\\\\\", false, path::Style::windows));
|
2017-03-16 23:28:04 +01:00
|
|
|
|
|
|
|
EXPECT_EQ("a\\..\\b\\c",
|
|
|
|
remove_dots(".\\a\\..\\b\\c", false, path::Style::windows));
|
|
|
|
EXPECT_EQ("b\\c", remove_dots(".\\a\\..\\b\\c", true, path::Style::windows));
|
|
|
|
EXPECT_EQ("c", remove_dots(".\\.\\c", true, path::Style::windows));
|
|
|
|
EXPECT_EQ("..\\a\\c",
|
|
|
|
remove_dots("..\\a\\b\\..\\c", true, path::Style::windows));
|
|
|
|
EXPECT_EQ("..\\..\\a\\c",
|
|
|
|
remove_dots("..\\..\\a\\b\\..\\c", true, path::Style::windows));
|
2015-11-09 20:36:53 +01:00
|
|
|
|
|
|
|
SmallString<64> Path1(".\\.\\c");
|
2017-03-16 23:28:04 +01:00
|
|
|
EXPECT_TRUE(path::remove_dots(Path1, true, path::Style::windows));
|
2015-11-09 19:56:31 +01:00
|
|
|
EXPECT_EQ("c", Path1);
|
2017-03-16 23:28:04 +01:00
|
|
|
|
|
|
|
EXPECT_EQ("foolz/wat",
|
|
|
|
remove_dots("././/foolz/wat", false, path::Style::posix));
|
2018-05-17 01:29:36 +02:00
|
|
|
EXPECT_EQ("", remove_dots("./////", false, path::Style::posix));
|
2017-03-16 23:28:04 +01:00
|
|
|
|
|
|
|
EXPECT_EQ("a/../b/c", remove_dots("./a/../b/c", false, path::Style::posix));
|
|
|
|
EXPECT_EQ("b/c", remove_dots("./a/../b/c", true, path::Style::posix));
|
|
|
|
EXPECT_EQ("c", remove_dots("././c", true, path::Style::posix));
|
|
|
|
EXPECT_EQ("../a/c", remove_dots("../a/b/../c", true, path::Style::posix));
|
|
|
|
EXPECT_EQ("../../a/c",
|
|
|
|
remove_dots("../../a/b/../c", true, path::Style::posix));
|
|
|
|
EXPECT_EQ("/a/c", remove_dots("/../../a/c", true, path::Style::posix));
|
|
|
|
EXPECT_EQ("/a/c",
|
|
|
|
remove_dots("/../a/b//../././/c", true, path::Style::posix));
|
|
|
|
|
|
|
|
SmallString<64> Path2("././c");
|
|
|
|
EXPECT_TRUE(path::remove_dots(Path2, true, path::Style::posix));
|
|
|
|
EXPECT_EQ("c", Path2);
|
2015-11-09 19:56:31 +01:00
|
|
|
}
|
2016-05-17 16:45:30 +02:00
|
|
|
|
|
|
|
TEST(Support, ReplacePathPrefix) {
|
|
|
|
SmallString<64> Path1("/foo");
|
|
|
|
SmallString<64> Path2("/old/foo");
|
|
|
|
SmallString<64> OldPrefix("/old");
|
|
|
|
SmallString<64> NewPrefix("/new");
|
|
|
|
SmallString<64> NewPrefix2("/longernew");
|
|
|
|
SmallString<64> EmptyPrefix("");
|
|
|
|
|
|
|
|
SmallString<64> Path = Path1;
|
|
|
|
path::replace_path_prefix(Path, OldPrefix, NewPrefix);
|
|
|
|
EXPECT_EQ(Path, "/foo");
|
|
|
|
Path = Path2;
|
|
|
|
path::replace_path_prefix(Path, OldPrefix, NewPrefix);
|
|
|
|
EXPECT_EQ(Path, "/new/foo");
|
|
|
|
Path = Path2;
|
|
|
|
path::replace_path_prefix(Path, OldPrefix, NewPrefix2);
|
|
|
|
EXPECT_EQ(Path, "/longernew/foo");
|
|
|
|
Path = Path1;
|
|
|
|
path::replace_path_prefix(Path, EmptyPrefix, NewPrefix);
|
|
|
|
EXPECT_EQ(Path, "/new/foo");
|
|
|
|
Path = Path2;
|
|
|
|
path::replace_path_prefix(Path, OldPrefix, EmptyPrefix);
|
|
|
|
EXPECT_EQ(Path, "/foo");
|
|
|
|
}
|
2016-06-13 17:54:56 +02:00
|
|
|
|
|
|
|
TEST_F(FileSystemTest, OpenFileForRead) {
|
|
|
|
// Create a temp file.
|
|
|
|
int FileDescriptor;
|
|
|
|
SmallString<64> TempPath;
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::createTemporaryFile("prefix", "temp", FileDescriptor, TempPath));
|
2016-09-02 02:51:34 +02:00
|
|
|
FileRemover Cleanup(TempPath);
|
2016-06-13 17:54:56 +02:00
|
|
|
|
|
|
|
// Make sure it exists.
|
|
|
|
ASSERT_TRUE(sys::fs::exists(Twine(TempPath)));
|
|
|
|
|
|
|
|
// Open the file for read
|
|
|
|
int FileDescriptor2;
|
|
|
|
SmallString<64> ResultPath;
|
2018-06-07 21:58:58 +02:00
|
|
|
ASSERT_NO_ERROR(fs::openFileForRead(Twine(TempPath), FileDescriptor2,
|
|
|
|
fs::OF_None, &ResultPath))
|
2016-06-13 17:54:56 +02:00
|
|
|
|
|
|
|
// If we succeeded, check that the paths are the same (modulo case):
|
|
|
|
if (!ResultPath.empty()) {
|
|
|
|
// The paths returned by createTemporaryFile and getPathFromOpenFD
|
|
|
|
// should reference the same file on disk.
|
|
|
|
fs::UniqueID D1, D2;
|
|
|
|
ASSERT_NO_ERROR(fs::getUniqueID(Twine(TempPath), D1));
|
|
|
|
ASSERT_NO_ERROR(fs::getUniqueID(Twine(ResultPath), D2));
|
|
|
|
ASSERT_EQ(D1, D2);
|
|
|
|
}
|
2018-07-04 16:17:10 +02:00
|
|
|
::close(FileDescriptor);
|
|
|
|
::close(FileDescriptor2);
|
2016-06-13 17:54:56 +02:00
|
|
|
|
2018-07-04 16:17:10 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
// Since Windows Vista, file access time is not updated by default.
|
|
|
|
// This is instead updated manually by openFileForRead.
|
|
|
|
// https://blogs.technet.microsoft.com/filecab/2006/11/07/disabling-last-access-time-in-windows-vista-to-improve-ntfs-performance/
|
|
|
|
// This part of the unit test is Windows specific as the updating of
|
|
|
|
// access times can be disabled on Linux using /etc/fstab.
|
|
|
|
|
|
|
|
// Set access time to UNIX epoch.
|
|
|
|
ASSERT_NO_ERROR(sys::fs::openFileForWrite(Twine(TempPath), FileDescriptor,
|
|
|
|
fs::CD_OpenExisting));
|
|
|
|
TimePoint<> Epoch(std::chrono::milliseconds(0));
|
2018-08-14 01:03:45 +02:00
|
|
|
ASSERT_NO_ERROR(fs::setLastAccessAndModificationTime(FileDescriptor, Epoch));
|
2016-06-13 17:54:56 +02:00
|
|
|
::close(FileDescriptor);
|
2018-07-04 16:17:10 +02:00
|
|
|
|
|
|
|
// Open the file and ensure access time is updated, when forced.
|
|
|
|
ASSERT_NO_ERROR(fs::openFileForRead(Twine(TempPath), FileDescriptor,
|
|
|
|
fs::OF_UpdateAtime, &ResultPath));
|
|
|
|
|
|
|
|
sys::fs::file_status Status;
|
|
|
|
ASSERT_NO_ERROR(sys::fs::status(FileDescriptor, Status));
|
|
|
|
auto FileAccessTime = Status.getLastAccessedTime();
|
|
|
|
|
|
|
|
ASSERT_NE(Epoch, FileAccessTime);
|
|
|
|
::close(FileDescriptor);
|
|
|
|
|
|
|
|
// Ideally this test would include a case when ATime is not forced to update,
|
|
|
|
// however the expected behaviour will differ depending on the configuration
|
|
|
|
// of the Windows file system.
|
|
|
|
#endif
|
2016-06-13 17:54:56 +02:00
|
|
|
}
|
2017-01-24 11:32:03 +01:00
|
|
|
|
2018-06-07 21:58:58 +02:00
|
|
|
static void createFileWithData(const Twine &Path, bool ShouldExistBefore,
|
|
|
|
fs::CreationDisposition Disp, StringRef Data) {
|
|
|
|
int FD;
|
|
|
|
ASSERT_EQ(ShouldExistBefore, fs::exists(Path));
|
|
|
|
ASSERT_NO_ERROR(fs::openFileForWrite(Path, FD, Disp));
|
|
|
|
FileDescriptorCloser Closer(FD);
|
|
|
|
ASSERT_TRUE(fs::exists(Path));
|
|
|
|
|
|
|
|
ASSERT_EQ(Data.size(), (size_t)write(FD, Data.data(), Data.size()));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void verifyFileContents(const Twine &Path, StringRef Contents) {
|
|
|
|
auto Buffer = MemoryBuffer::getFile(Path);
|
|
|
|
ASSERT_TRUE((bool)Buffer);
|
|
|
|
StringRef Data = Buffer.get()->getBuffer();
|
|
|
|
ASSERT_EQ(Data, Contents);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileSystemTest, CreateNew) {
|
|
|
|
int FD;
|
|
|
|
Optional<FileDescriptorCloser> Closer;
|
|
|
|
|
|
|
|
// Succeeds if the file does not exist.
|
|
|
|
ASSERT_FALSE(fs::exists(NonExistantFile));
|
|
|
|
ASSERT_NO_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateNew));
|
|
|
|
ASSERT_TRUE(fs::exists(NonExistantFile));
|
|
|
|
|
|
|
|
FileRemover Cleanup(NonExistantFile);
|
|
|
|
Closer.emplace(FD);
|
|
|
|
|
|
|
|
// And creates a file of size 0.
|
|
|
|
sys::fs::file_status Status;
|
|
|
|
ASSERT_NO_ERROR(sys::fs::status(FD, Status));
|
|
|
|
EXPECT_EQ(0ULL, Status.getSize());
|
|
|
|
|
|
|
|
// Close this first, before trying to re-open the file.
|
|
|
|
Closer.reset();
|
|
|
|
|
|
|
|
// But fails if the file does exist.
|
|
|
|
ASSERT_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateNew));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileSystemTest, CreateAlways) {
|
|
|
|
int FD;
|
|
|
|
Optional<FileDescriptorCloser> Closer;
|
|
|
|
|
|
|
|
// Succeeds if the file does not exist.
|
|
|
|
ASSERT_FALSE(fs::exists(NonExistantFile));
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateAlways));
|
|
|
|
|
|
|
|
Closer.emplace(FD);
|
|
|
|
|
|
|
|
ASSERT_TRUE(fs::exists(NonExistantFile));
|
|
|
|
|
|
|
|
FileRemover Cleanup(NonExistantFile);
|
|
|
|
|
|
|
|
// And creates a file of size 0.
|
|
|
|
uint64_t FileSize;
|
|
|
|
ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
|
|
|
|
ASSERT_EQ(0ULL, FileSize);
|
|
|
|
|
|
|
|
// If we write some data to it re-create it with CreateAlways, it succeeds and
|
|
|
|
// truncates to 0 bytes.
|
|
|
|
ASSERT_EQ(4, write(FD, "Test", 4));
|
|
|
|
|
|
|
|
Closer.reset();
|
|
|
|
|
|
|
|
ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
|
|
|
|
ASSERT_EQ(4ULL, FileSize);
|
|
|
|
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::openFileForWrite(NonExistantFile, FD, fs::CD_CreateAlways));
|
|
|
|
Closer.emplace(FD);
|
|
|
|
ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
|
|
|
|
ASSERT_EQ(0ULL, FileSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileSystemTest, OpenExisting) {
|
|
|
|
int FD;
|
|
|
|
|
|
|
|
// Fails if the file does not exist.
|
|
|
|
ASSERT_FALSE(fs::exists(NonExistantFile));
|
|
|
|
ASSERT_ERROR(fs::openFileForWrite(NonExistantFile, FD, fs::CD_OpenExisting));
|
|
|
|
ASSERT_FALSE(fs::exists(NonExistantFile));
|
|
|
|
|
|
|
|
// Make a dummy file now so that we can try again when the file does exist.
|
|
|
|
createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");
|
|
|
|
FileRemover Cleanup(NonExistantFile);
|
|
|
|
uint64_t FileSize;
|
|
|
|
ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
|
|
|
|
ASSERT_EQ(4ULL, FileSize);
|
|
|
|
|
|
|
|
// If we re-create it with different data, it overwrites rather than
|
|
|
|
// appending.
|
|
|
|
createFileWithData(NonExistantFile, true, fs::CD_OpenExisting, "Buzz");
|
|
|
|
verifyFileContents(NonExistantFile, "Buzz");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileSystemTest, OpenAlways) {
|
|
|
|
// Succeeds if the file does not exist.
|
|
|
|
createFileWithData(NonExistantFile, false, fs::CD_OpenAlways, "Fizz");
|
|
|
|
FileRemover Cleanup(NonExistantFile);
|
|
|
|
uint64_t FileSize;
|
|
|
|
ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
|
|
|
|
ASSERT_EQ(4ULL, FileSize);
|
|
|
|
|
|
|
|
// Now re-open it and write again, verifying the contents get over-written.
|
|
|
|
createFileWithData(NonExistantFile, true, fs::CD_OpenAlways, "Bu");
|
|
|
|
verifyFileContents(NonExistantFile, "Buzz");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileSystemTest, AppendSetsCorrectFileOffset) {
|
|
|
|
fs::CreationDisposition Disps[] = {fs::CD_CreateAlways, fs::CD_OpenAlways,
|
|
|
|
fs::CD_OpenExisting};
|
|
|
|
|
|
|
|
// Write some data and re-open it with every possible disposition (this is a
|
|
|
|
// hack that shouldn't work, but is left for compatibility. F_Append
|
|
|
|
// overrides
|
|
|
|
// the specified disposition.
|
|
|
|
for (fs::CreationDisposition Disp : Disps) {
|
|
|
|
int FD;
|
|
|
|
Optional<FileDescriptorCloser> Closer;
|
|
|
|
|
|
|
|
createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");
|
|
|
|
|
|
|
|
FileRemover Cleanup(NonExistantFile);
|
|
|
|
|
|
|
|
uint64_t FileSize;
|
|
|
|
ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
|
|
|
|
ASSERT_EQ(4ULL, FileSize);
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::openFileForWrite(NonExistantFile, FD, Disp, fs::OF_Append));
|
|
|
|
Closer.emplace(FD);
|
|
|
|
ASSERT_NO_ERROR(sys::fs::file_size(NonExistantFile, FileSize));
|
|
|
|
ASSERT_EQ(4ULL, FileSize);
|
|
|
|
|
|
|
|
ASSERT_EQ(4, write(FD, "Buzz", 4));
|
|
|
|
Closer.reset();
|
|
|
|
|
|
|
|
verifyFileContents(NonExistantFile, "FizzBuzz");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void verifyRead(int FD, StringRef Data, bool ShouldSucceed) {
|
|
|
|
std::vector<char> Buffer;
|
|
|
|
Buffer.resize(Data.size());
|
|
|
|
int Result = ::read(FD, Buffer.data(), Buffer.size());
|
|
|
|
if (ShouldSucceed) {
|
|
|
|
ASSERT_EQ((size_t)Result, Data.size());
|
|
|
|
ASSERT_EQ(Data, StringRef(Buffer.data(), Buffer.size()));
|
|
|
|
} else {
|
|
|
|
ASSERT_EQ(-1, Result);
|
|
|
|
ASSERT_EQ(EBADF, errno);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void verifyWrite(int FD, StringRef Data, bool ShouldSucceed) {
|
|
|
|
int Result = ::write(FD, Data.data(), Data.size());
|
|
|
|
if (ShouldSucceed)
|
|
|
|
ASSERT_EQ((size_t)Result, Data.size());
|
|
|
|
else {
|
|
|
|
ASSERT_EQ(-1, Result);
|
|
|
|
ASSERT_EQ(EBADF, errno);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileSystemTest, ReadOnlyFileCantWrite) {
|
|
|
|
createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");
|
|
|
|
FileRemover Cleanup(NonExistantFile);
|
|
|
|
|
|
|
|
int FD;
|
|
|
|
ASSERT_NO_ERROR(fs::openFileForRead(NonExistantFile, FD));
|
|
|
|
FileDescriptorCloser Closer(FD);
|
|
|
|
|
|
|
|
verifyWrite(FD, "Buzz", false);
|
|
|
|
verifyRead(FD, "Fizz", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileSystemTest, WriteOnlyFileCantRead) {
|
|
|
|
createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");
|
|
|
|
FileRemover Cleanup(NonExistantFile);
|
|
|
|
|
|
|
|
int FD;
|
|
|
|
ASSERT_NO_ERROR(
|
|
|
|
fs::openFileForWrite(NonExistantFile, FD, fs::CD_OpenExisting));
|
|
|
|
FileDescriptorCloser Closer(FD);
|
|
|
|
verifyRead(FD, "Fizz", false);
|
|
|
|
verifyWrite(FD, "Buzz", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(FileSystemTest, ReadWriteFileCanReadOrWrite) {
|
|
|
|
createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "Fizz");
|
|
|
|
FileRemover Cleanup(NonExistantFile);
|
|
|
|
|
|
|
|
int FD;
|
|
|
|
ASSERT_NO_ERROR(fs::openFileForReadWrite(NonExistantFile, FD,
|
|
|
|
fs::CD_OpenExisting, fs::OF_None));
|
|
|
|
FileDescriptorCloser Closer(FD);
|
|
|
|
verifyRead(FD, "Fizz", true);
|
|
|
|
verifyWrite(FD, "Buzz", true);
|
|
|
|
}
|
|
|
|
|
2017-01-24 11:32:03 +01:00
|
|
|
TEST_F(FileSystemTest, set_current_path) {
|
|
|
|
SmallString<128> path;
|
|
|
|
|
|
|
|
ASSERT_NO_ERROR(fs::current_path(path));
|
|
|
|
ASSERT_NE(TestDirectory, path);
|
|
|
|
|
|
|
|
struct RestorePath {
|
|
|
|
SmallString<128> path;
|
|
|
|
RestorePath(const SmallString<128> &path) : path(path) {}
|
|
|
|
~RestorePath() { fs::set_current_path(path); }
|
|
|
|
} restore_path(path);
|
|
|
|
|
|
|
|
ASSERT_NO_ERROR(fs::set_current_path(TestDirectory));
|
|
|
|
|
|
|
|
ASSERT_NO_ERROR(fs::current_path(path));
|
2017-01-24 12:35:26 +01:00
|
|
|
|
|
|
|
fs::UniqueID D1, D2;
|
|
|
|
ASSERT_NO_ERROR(fs::getUniqueID(TestDirectory, D1));
|
|
|
|
ASSERT_NO_ERROR(fs::getUniqueID(path, D2));
|
2017-03-13 13:24:51 +01:00
|
|
|
ASSERT_EQ(D1, D2) << "D1: " << TestDirectory << "\nD2: " << path;
|
|
|
|
}
|
|
|
|
|
[Support] Add support for getting file system permissions on Windows and implement sys::fs::set/getPermissions to work with them
This change adds support for functions to set and get file permissions, in a similar manner to the C++17 permissions() function in <filesystem>. The setter uses chmod on Unix systems and SetFileAttributes on Windows, setting the permissions as passed in. The getter simply uses the existing status() function.
Prior to this change, status() would always return an unknown value for the permissions on a Windows file, making it impossible to test the new function on Windows. I have therefore added support for this as well. On Linux, prior to this change, the permissions included the file type, which should actually be accessed via a different member of the file_status class.
Note that on Windows, only the *_write permission bits have any affect - if any are set, the file is writable, and if not, the file is read-only. This is in common with what MSDN describes for their behaviour of std::filesystem::permissions(), and also what boost::filesystem does.
The motivation behind this change is so that we can easily test behaviour on read-only files in LLVM unit tests, but I am sure that others may find it useful in some situations.
Reviewers: zturner, amccarth, aaron.ballman
Differential Revision: https://reviews.llvm.org/D30736
llvm-svn: 297945
2017-03-16 12:22:09 +01:00
|
|
|
TEST_F(FileSystemTest, permissions) {
|
|
|
|
int FD;
|
|
|
|
SmallString<64> TempPath;
|
|
|
|
ASSERT_NO_ERROR(fs::createTemporaryFile("prefix", "temp", FD, TempPath));
|
|
|
|
FileRemover Cleanup(TempPath);
|
|
|
|
|
|
|
|
// Make sure it exists.
|
|
|
|
ASSERT_TRUE(fs::exists(Twine(TempPath)));
|
|
|
|
|
|
|
|
auto CheckPermissions = [&](fs::perms Expected) {
|
|
|
|
ErrorOr<fs::perms> Actual = fs::getPermissions(TempPath);
|
|
|
|
return Actual && *Actual == Expected;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::error_code NoError;
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::all_all), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_all));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read | fs::all_exe), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_read | fs::all_exe));
|
|
|
|
|
2018-04-29 02:45:03 +02:00
|
|
|
#if defined(_WIN32)
|
[Support] Add support for getting file system permissions on Windows and implement sys::fs::set/getPermissions to work with them
This change adds support for functions to set and get file permissions, in a similar manner to the C++17 permissions() function in <filesystem>. The setter uses chmod on Unix systems and SetFileAttributes on Windows, setting the permissions as passed in. The getter simply uses the existing status() function.
Prior to this change, status() would always return an unknown value for the permissions on a Windows file, making it impossible to test the new function on Windows. I have therefore added support for this as well. On Linux, prior to this change, the permissions included the file type, which should actually be accessed via a different member of the file_status class.
Note that on Windows, only the *_write permission bits have any affect - if any are set, the file is writable, and if not, the file is read-only. This is in common with what MSDN describes for their behaviour of std::filesystem::permissions(), and also what boost::filesystem does.
The motivation behind this change is so that we can easily test behaviour on read-only files in LLVM unit tests, but I am sure that others may find it useful in some situations.
Reviewers: zturner, amccarth, aaron.ballman
Differential Revision: https://reviews.llvm.org/D30736
llvm-svn: 297945
2017-03-16 12:22:09 +01:00
|
|
|
fs::perms ReadOnly = fs::all_read | fs::all_exe;
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::no_perms), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_read), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_write), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_all));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_exe), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_all), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_all));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::group_read), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::group_write), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_all));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::group_exe), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::group_all), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_all));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::others_read), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::others_write), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_all));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::others_exe), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::others_all), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_all));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::all_write), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_all));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::all_exe), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::set_gid_on_exe), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::sticky_bit), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe |
|
|
|
|
fs::set_gid_on_exe |
|
|
|
|
fs::sticky_bit),
|
|
|
|
NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, ReadOnly | fs::set_uid_on_exe |
|
|
|
|
fs::set_gid_on_exe |
|
|
|
|
fs::sticky_bit),
|
|
|
|
NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(ReadOnly));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::all_perms), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_all));
|
|
|
|
#else
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::no_perms), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::no_perms));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_read), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::owner_read));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_write), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::owner_write));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_exe), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::owner_exe));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::owner_all), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::owner_all));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::group_read), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::group_read));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::group_write), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::group_write));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::group_exe), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::group_exe));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::group_all), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::group_all));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::others_read), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::others_read));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::others_write), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::others_write));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::others_exe), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::others_exe));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::others_all), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::others_all));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_read));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::all_write), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_write));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::all_exe), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_exe));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::set_uid_on_exe));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::set_gid_on_exe), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::set_gid_on_exe));
|
|
|
|
|
2017-04-24 20:54:48 +02:00
|
|
|
// Modern BSDs require root to set the sticky bit on files.
|
|
|
|
#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
|
[Support] Add support for getting file system permissions on Windows and implement sys::fs::set/getPermissions to work with them
This change adds support for functions to set and get file permissions, in a similar manner to the C++17 permissions() function in <filesystem>. The setter uses chmod on Unix systems and SetFileAttributes on Windows, setting the permissions as passed in. The getter simply uses the existing status() function.
Prior to this change, status() would always return an unknown value for the permissions on a Windows file, making it impossible to test the new function on Windows. I have therefore added support for this as well. On Linux, prior to this change, the permissions included the file type, which should actually be accessed via a different member of the file_status class.
Note that on Windows, only the *_write permission bits have any affect - if any are set, the file is writable, and if not, the file is read-only. This is in common with what MSDN describes for their behaviour of std::filesystem::permissions(), and also what boost::filesystem does.
The motivation behind this change is so that we can easily test behaviour on read-only files in LLVM unit tests, but I am sure that others may find it useful in some situations.
Reviewers: zturner, amccarth, aaron.ballman
Differential Revision: https://reviews.llvm.org/D30736
llvm-svn: 297945
2017-03-16 12:22:09 +01:00
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::sticky_bit), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::sticky_bit));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::set_uid_on_exe |
|
|
|
|
fs::set_gid_on_exe |
|
|
|
|
fs::sticky_bit),
|
|
|
|
NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::set_uid_on_exe | fs::set_gid_on_exe |
|
|
|
|
fs::sticky_bit));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::all_read | fs::set_uid_on_exe |
|
|
|
|
fs::set_gid_on_exe |
|
|
|
|
fs::sticky_bit),
|
|
|
|
NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_read | fs::set_uid_on_exe |
|
|
|
|
fs::set_gid_on_exe | fs::sticky_bit));
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::all_perms), NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_perms));
|
2017-04-24 20:54:48 +02:00
|
|
|
#endif // !FreeBSD && !NetBSD && !OpenBSD
|
|
|
|
|
|
|
|
EXPECT_EQ(fs::setPermissions(TempPath, fs::all_perms & ~fs::sticky_bit),
|
|
|
|
NoError);
|
|
|
|
EXPECT_TRUE(CheckPermissions(fs::all_perms & ~fs::sticky_bit));
|
[Support] Add support for getting file system permissions on Windows and implement sys::fs::set/getPermissions to work with them
This change adds support for functions to set and get file permissions, in a similar manner to the C++17 permissions() function in <filesystem>. The setter uses chmod on Unix systems and SetFileAttributes on Windows, setting the permissions as passed in. The getter simply uses the existing status() function.
Prior to this change, status() would always return an unknown value for the permissions on a Windows file, making it impossible to test the new function on Windows. I have therefore added support for this as well. On Linux, prior to this change, the permissions included the file type, which should actually be accessed via a different member of the file_status class.
Note that on Windows, only the *_write permission bits have any affect - if any are set, the file is writable, and if not, the file is read-only. This is in common with what MSDN describes for their behaviour of std::filesystem::permissions(), and also what boost::filesystem does.
The motivation behind this change is so that we can easily test behaviour on read-only files in LLVM unit tests, but I am sure that others may find it useful in some situations.
Reviewers: zturner, amccarth, aaron.ballman
Differential Revision: https://reviews.llvm.org/D30736
llvm-svn: 297945
2017-03-16 12:22:09 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-03-13 13:24:51 +01:00
|
|
|
} // anonymous namespace
|