mirror of
https://github.com/nestriness/cdc-file-transfer.git
synced 2026-01-30 20:05:37 +02:00
* Add a Github action for building and testing On Windows, -- -//third_party/... doesn't seem to work, so add all test directories manually. Also run the tests_*. We run only fastbuild tests here, since the opt tests will be run in the release workflow. Also fix a number of compilation and test issues found along the way.
1576 lines
52 KiB
C++
1576 lines
52 KiB
C++
// Copyright 2022 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "common/path.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdio>
|
|
#include <iterator>
|
|
#include <memory>
|
|
|
|
#include "absl/strings/match.h"
|
|
#include "common/buffer.h"
|
|
#include "common/log.h"
|
|
#include "common/platform.h"
|
|
#include "common/status.h"
|
|
#include "common/status_test_macros.h"
|
|
#include "common/test_main.h"
|
|
#include "common/util.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#if PLATFORM_WINDOWS
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN 1
|
|
#endif
|
|
#include <windows.h> // GetLongPathName
|
|
#undef ReplaceFile
|
|
#endif
|
|
|
|
namespace cdc_ft {
|
|
namespace {
|
|
|
|
// Clang-format tends to screw up the code following unicode string literals,
|
|
// so we just escape U+1F964 here.
|
|
constexpr char kUnicodeText[] = u8"\U0001F964\U0001F964\U0001F964";
|
|
constexpr char kTestText[] = "test text";
|
|
constexpr char kLinesText[] = R"!(
|
|
line
|
|
trailing whitespace
|
|
|
|
|
|
leading whitespace
|
|
)!";
|
|
|
|
constexpr char kBaseDirName[] = "__path_unittest_base";
|
|
constexpr char kSearchDirName[] = "search";
|
|
constexpr char kSubDirName[] = "subdir";
|
|
|
|
constexpr char kLinesFileName[] = "lines_test.txt";
|
|
constexpr char kTestFileName[] = "test.txt";
|
|
constexpr char kUnicodeTestFileName[] = u8"unicode test \U0001F964.txt";
|
|
constexpr char kSubDirFileName[] = "subdir_file.txt";
|
|
|
|
// Converts e.g. C:\Progra~1 to C:\Program Files on Windows.
|
|
std::string GetLongPath(std::string path) {
|
|
#if PLATFORM_WINDOWS
|
|
std::wstring wpath = Util::Utf8ToWideStr(path);
|
|
std::wstring long_wpath(4096, 0);
|
|
size_t buf_len =
|
|
GetLongPathName(wpath.c_str(), const_cast<wchar_t*>(long_wpath.c_str()),
|
|
long_wpath.size() * sizeof(wchar_t));
|
|
EXPECT_GT(buf_len, 0);
|
|
long_wpath.resize(buf_len);
|
|
return Util::WideToUtf8Str(long_wpath);
|
|
#else
|
|
return path;
|
|
#endif
|
|
}
|
|
|
|
class PathTest : public ::testing::Test {
|
|
public:
|
|
PathTest() {
|
|
path::EnsureEndsWithPathSeparator(&base_dir_);
|
|
path::EnsureEndsWithPathSeparator(&search_dir_);
|
|
path::EnsureEndsWithPathSeparator(&subdir_);
|
|
}
|
|
|
|
static void SetUpTestSuite() {
|
|
const std::string base_dir = path::Join(path::GetTempDir(), kBaseDirName);
|
|
const std::string search_dir = path::Join(base_dir, kSearchDirName);
|
|
const std::string subdir = path::Join(search_dir, kSubDirName);
|
|
|
|
const std::string lines_filepath = path::Join(base_dir, kLinesFileName);
|
|
const std::string test_filepath = path::Join(search_dir, kTestFileName);
|
|
const std::string unicode_test_filepath =
|
|
path::Join(search_dir, kUnicodeTestFileName);
|
|
const std::string subdir_filepath = path::Join(subdir, kSubDirFileName);
|
|
|
|
// Create test data. Note that since Bazel doesn't support unicode paths, we
|
|
// can't use runfiles.
|
|
EXPECT_OK(path::RemoveDirRec(base_dir));
|
|
EXPECT_OK(path::CreateDirRec(subdir));
|
|
WriteStringToFile(lines_filepath, kLinesText);
|
|
WriteStringToFile(test_filepath, kTestText);
|
|
WriteStringToFile(unicode_test_filepath, kUnicodeText);
|
|
WriteStringToFile(subdir_filepath, kTestText);
|
|
}
|
|
|
|
static void TearDownTestSuite() {
|
|
const std::string base_dir = path::Join(path::GetTempDir(), kBaseDirName);
|
|
EXPECT_OK(path::RemoveDirRec(base_dir));
|
|
}
|
|
|
|
void SetUp() override {
|
|
Log::Initialize(std::make_unique<ConsoleLog>(LogLevel::kInfo));
|
|
|
|
// Clean up possible left-overs from previous runs (in case TearDown()
|
|
// didn't run, e.g. when the process was killed).
|
|
ForceRemoveFile(tmp_path_1_);
|
|
ForceRemoveFile(tmp_path_2_);
|
|
}
|
|
|
|
void TearDown() override {
|
|
// Clean up.
|
|
ForceRemoveFile(tmp_path_1_);
|
|
ForceRemoveFile(tmp_path_2_);
|
|
|
|
Log::Shutdown();
|
|
}
|
|
|
|
protected:
|
|
struct File {
|
|
std::string dir_;
|
|
std::string filename_;
|
|
bool is_directory_ = false;
|
|
std::string Path() const { return path::Join(dir_, filename_); }
|
|
};
|
|
|
|
static void WriteStringToFile(const std::string& path,
|
|
const std::string& contents) {
|
|
Buffer b;
|
|
b.resize(contents.size());
|
|
memcpy(b.data(), contents.data(), contents.size());
|
|
EXPECT_OK(path::WriteFile(path, b));
|
|
}
|
|
|
|
// Loads "read_all_lines_test.txt" with path::ReadAllLines and the given
|
|
// |flags| and verifies that the |expected| lines were read.
|
|
void CheckReadAllLines(path::ReadFlags flags,
|
|
std::vector<std::string> expected) {
|
|
std::vector<std::string> lines;
|
|
EXPECT_OK(path::ReadAllLines(lines_filepath_, &lines, flags));
|
|
ASSERT_EQ(lines.size(), expected.size());
|
|
for (size_t n = 0; n < lines.size(); ++n) {
|
|
EXPECT_EQ(lines[n], expected[n]);
|
|
}
|
|
}
|
|
|
|
// Reads from |test_filepath_| in pieces of |buffer_size| bytes and verifies
|
|
// that the data read matches |expected_data|. Uses StreamReadFileContents().
|
|
void CheckStreamReadFileContents(
|
|
size_t buffer_size, std::vector<std::string> expected_data) const;
|
|
|
|
// Removes the write permissions from the file at |path|.
|
|
void RemoveWritePermission(const std::string& path) {
|
|
path::Stats stats;
|
|
EXPECT_OK(path::GetStats(path, &stats));
|
|
EXPECT_OK(path::ChangeMode(path, stats.mode & ~path::MODE_IWUSR));
|
|
}
|
|
|
|
// Removes the file at |path|, even if it is write-protected.
|
|
void ForceRemoveFile(const std::string& path) {
|
|
if (!path::Exists(path)) {
|
|
return;
|
|
}
|
|
|
|
// Add write permission, so file can be removed.
|
|
path::Stats stats;
|
|
EXPECT_OK(path::GetStats(path, &stats));
|
|
EXPECT_OK(path::ChangeMode(path, stats.mode | path::MODE_IWUSR));
|
|
EXPECT_OK(path::RemoveFile(path));
|
|
}
|
|
|
|
std::vector<File> SearchFiles(const std::string& pattern, bool recursive);
|
|
|
|
// Some tests get confused if the temp dir is a short filename
|
|
// (e.g. RUNNER~1 instead of runneradmin - I'm looking at you, Github!).
|
|
const std::string tmp_dir_ = GetLongPath(path::GetTempDir());
|
|
std::string base_dir_ = path::Join(tmp_dir_, kBaseDirName);
|
|
std::string search_dir_ = path::Join(base_dir_, kSearchDirName);
|
|
std::string subdir_ = path::Join(search_dir_, kSubDirName);
|
|
const std::string subdir_fulldirpath_ = subdir_;
|
|
|
|
const std::string lines_filepath_ = path::Join(base_dir_, kLinesFileName);
|
|
const std::string test_filepath_ = path::Join(search_dir_, kTestFileName);
|
|
const std::string unicode_test_filepath_ =
|
|
path::Join(search_dir_, kUnicodeTestFileName);
|
|
const std::string subdir_filepath_ = path::Join(subdir_, kSubDirFileName);
|
|
|
|
const std::string tmp_path_1_ = path::Join(base_dir_, "a.txt");
|
|
const std::string tmp_path_2_ = path::Join(base_dir_, "b.txt");
|
|
};
|
|
|
|
TEST_F(PathTest, GetExeDir) {
|
|
std::string exe_dir;
|
|
EXPECT_OK(path::GetExeDir(&exe_dir));
|
|
|
|
// We don't know the expected path, but at least we can test that we have a
|
|
// valid path consisting of at least one directory and a file name.
|
|
std::vector<absl::string_view> parts =
|
|
SplitString(exe_dir, path::PathSeparator(), false);
|
|
EXPECT_GE(parts.size(), 1) << exe_dir;
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS
|
|
TEST_F(PathTest, GetKnownFolderPath) {
|
|
std::string program_files;
|
|
EXPECT_OK(
|
|
path::GetKnownFolderPath(path::FolderId::kProgramFiles, &program_files));
|
|
EXPECT_NE(program_files.find("Program"), std::string::npos);
|
|
|
|
std::string appdata;
|
|
EXPECT_OK(
|
|
path::GetKnownFolderPath(path::FolderId::kRoamingAppData, &appdata));
|
|
EXPECT_NE(appdata.find("Roaming"), std::string::npos);
|
|
}
|
|
#endif
|
|
|
|
TEST_F(PathTest, ExpandPathVariables) {
|
|
#if PLATFORM_WINDOWS
|
|
std::string path = u8"%userPROfile%\\fOo\U0001F964";
|
|
EXPECT_OK(path::ExpandPathVariables(&path));
|
|
EXPECT_TRUE(absl::StartsWith(path, "C:\\Users\\")) << path;
|
|
EXPECT_TRUE(absl::EndsWith(path, u8"\\fOo\U0001F964")) << path;
|
|
|
|
path = "%ProgramFiles(x86)%\\Foo";
|
|
EXPECT_OK(path::ExpandPathVariables(&path));
|
|
EXPECT_EQ(path, "C:\\Program Files (x86)\\Foo");
|
|
|
|
path = "%unrelated%\\fOo";
|
|
EXPECT_OK(path::ExpandPathVariables(&path));
|
|
EXPECT_EQ(path, "%unrelated%\\fOo") << path;
|
|
#else
|
|
std::string path = u8"fooU0001F964";
|
|
EXPECT_OK(path::ExpandPathVariables(&path));
|
|
EXPECT_EQ(path, u8"fooU0001F964");
|
|
|
|
path = "~/foo";
|
|
EXPECT_OK(path::ExpandPathVariables(&path));
|
|
EXPECT_TRUE(absl::StrContains(path, "home")) << path;
|
|
|
|
path = "*";
|
|
EXPECT_ERROR_MSG(InvalidArgument, "multiple results",
|
|
path::ExpandPathVariables(&path));
|
|
#endif
|
|
}
|
|
|
|
TEST_F(PathTest, GetEnv_DoesNotExist) {
|
|
std::string value;
|
|
EXPECT_TRUE(absl::IsNotFound(
|
|
path::GetEnv("PRETTY_SURE_THIS_DOES_NOT_EXIST", &value)));
|
|
EXPECT_TRUE(value.empty());
|
|
}
|
|
|
|
TEST_F(PathTest, SetEnv_Empty) {
|
|
const std::string name = "env name";
|
|
const std::string empty_value;
|
|
EXPECT_OK(path::SetEnv(name, empty_value));
|
|
|
|
std::string value;
|
|
EXPECT_OK(path::GetEnv(name, &value));
|
|
EXPECT_EQ(value, empty_value);
|
|
}
|
|
|
|
TEST_F(PathTest, SetEnv_NonEmpty) {
|
|
const std::string name = "env name";
|
|
const std::string expected_value = u8"some unicode \U0001F964 value";
|
|
EXPECT_OK(path::SetEnv(name, expected_value));
|
|
|
|
std::string value;
|
|
EXPECT_OK(path::GetEnv(name, &value));
|
|
EXPECT_EQ(value, expected_value);
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS
|
|
TEST_F(PathTest, GetDrivePrefix) {
|
|
EXPECT_EQ(path::GetDrivePrefix(""), "");
|
|
EXPECT_EQ(path::GetDrivePrefix("\\"), "");
|
|
EXPECT_EQ(path::GetDrivePrefix("\\\\"), "");
|
|
EXPECT_EQ(path::GetDrivePrefix("\\\\shr"), "");
|
|
EXPECT_EQ(path::GetDrivePrefix("\\\\shr"), "");
|
|
EXPECT_EQ(path::GetDrivePrefix("\\\\\\"), "");
|
|
EXPECT_EQ(path::GetDrivePrefix("\\\\cpu\\"), "");
|
|
EXPECT_EQ(path::GetDrivePrefix("\\\\cpu\\\\"), "");
|
|
EXPECT_EQ(path::GetDrivePrefix("\\\\cpu\\\\dir\\file"), "");
|
|
EXPECT_EQ(path::GetDrivePrefix("\\\\cpu\\shr"), "\\\\cpu\\shr");
|
|
EXPECT_EQ(path::GetDrivePrefix("\\\\cpu\\shr\\"), "\\\\cpu\\shr");
|
|
EXPECT_EQ(path::GetDrivePrefix("\\\\cpu\\shr\\dir\\file"), "\\\\cpu\\shr");
|
|
EXPECT_EQ(path::GetDrivePrefix("\\dir"), "");
|
|
EXPECT_EQ(path::GetDrivePrefix("C"), "");
|
|
EXPECT_EQ(path::GetDrivePrefix("C:"), "C:");
|
|
EXPECT_EQ(path::GetDrivePrefix("C:\\"), "C:");
|
|
EXPECT_EQ(path::GetDrivePrefix("C:\\dir"), "C:");
|
|
EXPECT_EQ(path::GetDrivePrefix("C:\\dir\\file"), "C:");
|
|
}
|
|
#endif
|
|
|
|
TEST_F(PathTest, GetCwd) {
|
|
// Hmm, how to check properly?
|
|
std::string cwd = path::GetCwd();
|
|
EXPECT_FALSE(cwd.empty());
|
|
}
|
|
|
|
TEST_F(PathTest, GetFullPath) {
|
|
// Canonicalize the cwd to match the original directory capitalization.
|
|
std::string cwd = path::GetFullPath(path::GetCwd());
|
|
path::EnsureDoesNotEndWithPathSeparator(&cwd);
|
|
|
|
std::string parent_dir = path::DirName(cwd);
|
|
path::EnsureDoesNotEndWithPathSeparator(&parent_dir);
|
|
|
|
EXPECT_EQ(path::GetFullPath(""), "");
|
|
EXPECT_EQ(path::GetFullPath("."), cwd);
|
|
EXPECT_EQ(path::GetFullPath(".."), parent_dir);
|
|
EXPECT_EQ(path::GetFullPath("foo"), path::Join(cwd, "foo"));
|
|
EXPECT_EQ(path::GetFullPath(unicode_test_filepath_), unicode_test_filepath_);
|
|
EXPECT_EQ(path::GetFullPath(kUnicodeTestFileName),
|
|
path::Join(cwd, kUnicodeTestFileName));
|
|
EXPECT_EQ(path::GetFullPath(path::Join(u8"\U0001F964", "foo")),
|
|
path::Join(cwd, u8"\U0001F964", "foo"));
|
|
|
|
#if PLATFORM_WINDOWS
|
|
std::string current_drive = path::GetDrivePrefix(cwd);
|
|
ASSERT_NE(current_drive, std::string());
|
|
ASSERT_EQ(current_drive.size(), 2) << current_drive;
|
|
EXPECT_EQ(path::GetFullPath(current_drive), cwd);
|
|
EXPECT_EQ(path::GetFullPath(current_drive + "foo"), path::Join(cwd, "foo"));
|
|
EXPECT_EQ(path::GetFullPath("V:foo"), "V:\\foo");
|
|
EXPECT_EQ(path::GetFullPath("C:\\"), "C:\\");
|
|
EXPECT_EQ(path::GetFullPath("C:\\foo"), "C:\\foo");
|
|
EXPECT_EQ(path::GetFullPath("C:\\foo\\bar"), "C:\\foo\\bar");
|
|
EXPECT_EQ(path::GetFullPath("foo"), path::Join(cwd, "foo"));
|
|
EXPECT_EQ(path::GetFullPath(".\\foo"), path::Join(cwd, "foo"));
|
|
EXPECT_EQ(path::GetFullPath(".\\.\\foo"), path::Join(cwd, "foo"));
|
|
EXPECT_EQ(path::GetFullPath("..\\foo"), path::Join(parent_dir, "foo"));
|
|
EXPECT_EQ(path::GetFullPath("..\\.\\foo"), path::Join(parent_dir, "foo"));
|
|
EXPECT_EQ(path::GetFullPath(".\\..\\foo"), path::Join(parent_dir, "foo"));
|
|
EXPECT_EQ(path::GetFullPath("foo\\.\\bar"), path::Join(cwd, "foo", "bar"));
|
|
EXPECT_EQ(path::GetFullPath("foo\\..\\bar"), path::Join(cwd, "bar"));
|
|
// It is possible to create directories with trailing spaces via APIs, but
|
|
// other Windows APIs are trimming such spaces, most notably GetFullPathNameW.
|
|
EXPECT_EQ(path::GetFullPath("trailing space "),
|
|
path::Join(cwd, "trailing space "));
|
|
EXPECT_EQ(path::GetFullPath(current_drive + "trailing space "),
|
|
path::Join(cwd, "trailing space "));
|
|
EXPECT_EQ(path::GetFullPath(current_drive + "\\trailing space "),
|
|
current_drive + "\\trailing space ");
|
|
EXPECT_EQ(path::GetFullPath("V:trailing space "), "V:\\trailing space ");
|
|
#else
|
|
EXPECT_EQ(path::GetFullPath("/"), "/");
|
|
EXPECT_EQ(path::GetFullPath("/tmp"), "/tmp");
|
|
EXPECT_EQ(path::GetFullPath("/tmp/foo"), "/tmp/foo");
|
|
EXPECT_EQ(path::GetFullPath("/tmp/foo/bar"), "/tmp/foo/bar");
|
|
EXPECT_EQ(path::GetFullPath("./foo"), path::Join(cwd, "foo"));
|
|
EXPECT_EQ(path::GetFullPath("././foo"), path::Join(cwd, "foo"));
|
|
EXPECT_EQ(path::GetFullPath("../foo"), path::Join(parent_dir, "foo"));
|
|
EXPECT_EQ(path::GetFullPath(".././foo"), path::Join(parent_dir, "foo"));
|
|
EXPECT_EQ(path::GetFullPath("./../foo"), path::Join(parent_dir, "foo"));
|
|
EXPECT_EQ(path::GetFullPath("foo/./bar"), path::Join(cwd, "foo", "bar"));
|
|
EXPECT_EQ(path::GetFullPath("foo/../bar"), path::Join(cwd, "bar"));
|
|
#endif
|
|
}
|
|
|
|
TEST_F(PathTest, IsAbsolute) {
|
|
EXPECT_FALSE(path::IsAbsolute(""));
|
|
EXPECT_FALSE(path::IsAbsolute("."));
|
|
EXPECT_FALSE(path::IsAbsolute(".."));
|
|
EXPECT_FALSE(path::IsAbsolute("foo"));
|
|
EXPECT_TRUE(path::IsAbsolute(unicode_test_filepath_));
|
|
EXPECT_FALSE(path::IsAbsolute(kUnicodeTestFileName));
|
|
EXPECT_FALSE(path::IsAbsolute(path::Join(u8"\U0001F964", "foo")));
|
|
|
|
#if PLATFORM_WINDOWS
|
|
EXPECT_FALSE(path::IsAbsolute("C:"));
|
|
EXPECT_FALSE(path::IsAbsolute("C:foo"));
|
|
EXPECT_FALSE(path::IsAbsolute("V:foo"));
|
|
EXPECT_TRUE(path::IsAbsolute("C:\\"));
|
|
EXPECT_TRUE(path::IsAbsolute("C:\\foo"));
|
|
EXPECT_TRUE(path::IsAbsolute("C:\\foo\\bar"));
|
|
EXPECT_TRUE(path::IsAbsolute("V:\\"));
|
|
EXPECT_TRUE(path::IsAbsolute("V:\\foo"));
|
|
EXPECT_TRUE(path::IsAbsolute("V:\\foo\\bar"));
|
|
EXPECT_FALSE(path::IsAbsolute("foo"));
|
|
EXPECT_FALSE(path::IsAbsolute(".\\foo"));
|
|
EXPECT_FALSE(path::IsAbsolute(".\\.\\foo"));
|
|
EXPECT_FALSE(path::IsAbsolute("..\\foo"));
|
|
EXPECT_FALSE(path::IsAbsolute("..\\.\\foo"));
|
|
EXPECT_FALSE(path::IsAbsolute(".\\..\\foo"));
|
|
EXPECT_FALSE(path::IsAbsolute("foo\\.\\bar"));
|
|
EXPECT_FALSE(path::IsAbsolute("foo\\..\\bar"));
|
|
#else
|
|
EXPECT_TRUE(path::IsAbsolute("/"));
|
|
EXPECT_TRUE(path::IsAbsolute("/foo"));
|
|
EXPECT_TRUE(path::IsAbsolute("/foo/bar"));
|
|
EXPECT_FALSE(path::IsAbsolute("foo"));
|
|
EXPECT_FALSE(path::IsAbsolute("foo/bar"));
|
|
EXPECT_FALSE(path::IsAbsolute("."));
|
|
EXPECT_FALSE(path::IsAbsolute("./foo"));
|
|
EXPECT_FALSE(path::IsAbsolute("./foo/bar"));
|
|
EXPECT_FALSE(path::IsAbsolute("foo/./bar"));
|
|
EXPECT_TRUE(path::IsAbsolute("/."));
|
|
EXPECT_TRUE(path::IsAbsolute("/./foo"));
|
|
EXPECT_TRUE(path::IsAbsolute("/./foo/bar"));
|
|
EXPECT_TRUE(path::IsAbsolute("/foo/../bar"));
|
|
#endif
|
|
}
|
|
|
|
TEST_F(PathTest, DirName) {
|
|
EXPECT_EQ(path::DirName(""), "");
|
|
EXPECT_EQ(path::DirName("/"), "/");
|
|
EXPECT_EQ(path::DirName("/\\//"), "/\\//");
|
|
EXPECT_EQ(path::DirName("/foo"), "/");
|
|
EXPECT_EQ(path::DirName("/foo/"), "/");
|
|
EXPECT_EQ(path::DirName("//foo/"), "//");
|
|
EXPECT_EQ(path::DirName("foo"), "");
|
|
EXPECT_EQ(path::DirName("foo/"), "");
|
|
EXPECT_EQ(path::DirName("foo/bar"), "foo");
|
|
EXPECT_EQ(path::DirName("foo/bar/"), "foo");
|
|
EXPECT_EQ(path::DirName("foo///bar///"), "foo");
|
|
EXPECT_EQ(path::DirName("foo\\bar"), "foo");
|
|
EXPECT_EQ(path::DirName("foo\\bar\\"), "foo");
|
|
EXPECT_EQ(path::DirName("foo/bar/baz"), "foo/bar");
|
|
EXPECT_EQ(path::DirName("C:\\foo\\bar"), "C:\\foo");
|
|
EXPECT_EQ(path::DirName("C:\\foo\\bar\\"), "C:\\foo");
|
|
EXPECT_EQ(path::DirName("C:/foo/bar"), "C:/foo");
|
|
EXPECT_EQ(path::DirName("C:/foo\\bar"), "C:/foo");
|
|
EXPECT_EQ(path::DirName("C:/foo\\\\bar//"), "C:/foo");
|
|
EXPECT_EQ(path::DirName("C:\\foo\\bar\\baz"), "C:\\foo\\bar");
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS
|
|
TEST_F(PathTest, DirNameWinSpecials) {
|
|
// Handling of the drive letter is special on Windows.
|
|
EXPECT_EQ(path::DirName("C:\\"), "C:\\");
|
|
EXPECT_EQ(path::DirName("C:"), "C:");
|
|
EXPECT_EQ(path::DirName("C:foo"), "C:");
|
|
EXPECT_EQ(path::DirName("C:\\foo"), "C:\\");
|
|
EXPECT_EQ(path::DirName("C:\\foo\\"), "C:\\");
|
|
// Network shares are treated like drive prefixes on Windows.
|
|
EXPECT_EQ(path::DirName("\\\\comp\\share"), "\\\\comp\\share");
|
|
EXPECT_EQ(path::DirName("\\\\comp\\share\\"), "\\\\comp\\share\\");
|
|
EXPECT_EQ(path::DirName("\\\\comp\\share\\foo\\bar"), "\\\\comp\\share\\foo");
|
|
}
|
|
#endif
|
|
|
|
TEST_F(PathTest, BaseName) {
|
|
EXPECT_EQ(path::BaseName(""), "");
|
|
EXPECT_EQ(path::BaseName("/"), "");
|
|
EXPECT_EQ(path::BaseName("//"), "");
|
|
EXPECT_EQ(path::BaseName("\\"), "");
|
|
EXPECT_EQ(path::BaseName("\\\\"), "");
|
|
EXPECT_EQ(path::BaseName("\\/"), "");
|
|
EXPECT_EQ(path::BaseName("foo/bar"), "bar");
|
|
EXPECT_EQ(path::BaseName("foo/bar/"), "bar");
|
|
EXPECT_EQ(path::BaseName("foo/bar///"), "bar");
|
|
EXPECT_EQ(path::BaseName("foo\\bar"), "bar");
|
|
EXPECT_EQ(path::BaseName("foo\\bar\\"), "bar");
|
|
EXPECT_EQ(path::BaseName("foo\\\\bar\\\\\\"), "bar");
|
|
EXPECT_EQ(path::BaseName("foo/bar\\"), "bar");
|
|
EXPECT_EQ(path::BaseName("foo\\bar/"), "bar");
|
|
EXPECT_EQ(path::BaseName("foo\\bar\\/"), "bar");
|
|
EXPECT_EQ(path::BaseName("foo"), "foo");
|
|
EXPECT_EQ(path::BaseName("foo/"), "foo");
|
|
EXPECT_EQ(path::BaseName("/foo"), "foo");
|
|
EXPECT_EQ(path::BaseName("/foo/"), "foo");
|
|
EXPECT_EQ(path::BaseName("foo/bar/baz"), "baz");
|
|
// Windows drives should probably be handled differently.
|
|
EXPECT_EQ(path::BaseName("C:"), "C:");
|
|
EXPECT_EQ(path::BaseName("C:\\"), "C:");
|
|
EXPECT_EQ(path::BaseName("C:\\foo"), "foo");
|
|
EXPECT_EQ(path::BaseName("C:\\foo\\bar"), "bar");
|
|
EXPECT_EQ(path::BaseName("C:\\foo\\bar\\"), "bar");
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS
|
|
TEST_F(PathTest, Join) {
|
|
const char* expected = "C:\\data\\file";
|
|
EXPECT_EQ(path::Join("C:\\data", "file"), expected);
|
|
EXPECT_EQ(path::Join("C:\\data\\", "file"), expected);
|
|
EXPECT_EQ(path::Join("C:\\data/", "file"), "C:\\data/file");
|
|
EXPECT_EQ(path::Join("", "file"), "file");
|
|
EXPECT_EQ(path::Join("C:\\data", ""), "C:\\data");
|
|
|
|
expected = "C:\\data\\dir\\file";
|
|
EXPECT_EQ(path::Join("C:\\data", "dir", "file"), expected);
|
|
EXPECT_EQ(path::Join("C:\\data\\", "dir", "file"), expected);
|
|
EXPECT_EQ(path::Join("C:\\data", "dir\\", "file"), expected);
|
|
EXPECT_EQ(path::Join("C:\\data\\", "dir\\", "file"), expected);
|
|
|
|
// Don't bother testing all 8.
|
|
expected = "C:\\data\\dir1\\dir2\\file";
|
|
EXPECT_EQ(path::Join("C:\\data", "dir1", "dir2", "file"), expected);
|
|
EXPECT_EQ(path::Join("C:\\data\\", "dir1", "dir2", "file"), expected);
|
|
EXPECT_EQ(path::Join("C:\\data", "dir1\\", "dir2", "file"), expected);
|
|
EXPECT_EQ(path::Join("C:\\data", "dir1", "dir2\\", "file"), expected);
|
|
}
|
|
#endif
|
|
|
|
#if PLATFORM_LINUX
|
|
TEST_F(PathTest, Join) {
|
|
const char* expected = "/data/file";
|
|
EXPECT_EQ(path::Join("/data", "file"), expected);
|
|
EXPECT_EQ(path::Join("/data/", "file"), expected);
|
|
EXPECT_EQ(path::Join("/data\\", "file"), "/data\\file");
|
|
EXPECT_EQ(path::Join("", "file"), "file");
|
|
EXPECT_EQ(path::Join("/data", ""), "/data");
|
|
|
|
expected = "/data/dir/file";
|
|
EXPECT_EQ(path::Join("/data", "dir", "file"), expected);
|
|
EXPECT_EQ(path::Join("/data/", "dir", "file"), expected);
|
|
EXPECT_EQ(path::Join("/data", "dir/", "file"), expected);
|
|
EXPECT_EQ(path::Join("/data/", "dir/", "file"), expected);
|
|
|
|
// Don't bother testing all 8.
|
|
expected = "/data/dir1/dir2/file";
|
|
EXPECT_EQ(path::Join("/data", "dir1", "dir2", "file"), expected);
|
|
EXPECT_EQ(path::Join("/data/", "dir1", "dir2", "file"), expected);
|
|
EXPECT_EQ(path::Join("/data", "dir1/", "dir2", "file"), expected);
|
|
EXPECT_EQ(path::Join("/data", "dir1", "dir2/", "file"), expected);
|
|
}
|
|
#endif
|
|
|
|
#if PLATFORM_WINDOWS
|
|
TEST_F(PathTest, Append) {
|
|
const char* expected = "C:\\data\\file";
|
|
|
|
std::string path = "C:\\data";
|
|
path::Append(&path, "file");
|
|
EXPECT_EQ(path, expected);
|
|
|
|
path = "C:\\data\\";
|
|
path::Append(&path, "file");
|
|
EXPECT_EQ(path, expected);
|
|
|
|
path = "C:\\data/";
|
|
path::Append(&path, "file");
|
|
EXPECT_EQ(path, "C:\\data/file");
|
|
|
|
path = "";
|
|
path::Append(&path, "file");
|
|
EXPECT_EQ(path, "file");
|
|
|
|
path = "C:\\data";
|
|
path::Append(&path, "");
|
|
EXPECT_EQ(path, "C:\\data");
|
|
}
|
|
#endif
|
|
|
|
#if PLATFORM_LINUX
|
|
TEST_F(PathTest, Append) {
|
|
const char* expected = "/data/file";
|
|
|
|
std::string path = "/data";
|
|
path::Append(&path, "file");
|
|
EXPECT_EQ(path, expected);
|
|
|
|
path = "/data/";
|
|
path::Append(&path, "file");
|
|
EXPECT_EQ(path, expected);
|
|
|
|
path = "C:\\data\\";
|
|
path::Append(&path, "file");
|
|
EXPECT_EQ(path, "C:\\data\\file");
|
|
|
|
path = "";
|
|
path::Append(&path, "file");
|
|
EXPECT_EQ(path, "file");
|
|
|
|
path = "/data";
|
|
path::Append(&path, "");
|
|
EXPECT_EQ(path, "/data");
|
|
}
|
|
#endif
|
|
|
|
TEST_F(PathTest, AppendUnix) {
|
|
const char* expected = "/data/file";
|
|
|
|
std::string path = "/data";
|
|
path::AppendUnix(&path, "file");
|
|
EXPECT_EQ(path, expected);
|
|
|
|
path = "/data/";
|
|
path::AppendUnix(&path, "file");
|
|
EXPECT_EQ(path, expected);
|
|
|
|
path = "/data\\";
|
|
path::AppendUnix(&path, "file");
|
|
EXPECT_EQ(path, "/data\\file");
|
|
|
|
path = "";
|
|
path::AppendUnix(&path, "file");
|
|
EXPECT_EQ(path, "file");
|
|
|
|
path = "/data";
|
|
path::AppendUnix(&path, "");
|
|
EXPECT_EQ(path, "/data");
|
|
}
|
|
|
|
TEST_F(PathTest, EndsWithPathSeparator) {
|
|
EXPECT_FALSE(path::EndsWithPathSeparator(""));
|
|
EXPECT_FALSE(path::EndsWithPathSeparator("C:\\data"));
|
|
EXPECT_FALSE(path::EndsWithPathSeparator("/data"));
|
|
EXPECT_TRUE(path::EndsWithPathSeparator("C:\\data\\"));
|
|
EXPECT_TRUE(path::EndsWithPathSeparator("C:\\data/"));
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS
|
|
TEST_F(PathTest, EnsureEndsWithPathSeparator) {
|
|
std::string path = "C:\\data";
|
|
path::EnsureEndsWithPathSeparator(&path);
|
|
EXPECT_EQ(path, "C:\\data\\");
|
|
path::EnsureEndsWithPathSeparator(&path);
|
|
EXPECT_EQ(path, "C:\\data\\");
|
|
|
|
path = "/data/";
|
|
path::EnsureEndsWithPathSeparator(&path);
|
|
EXPECT_EQ(path, "/data/");
|
|
}
|
|
#endif
|
|
|
|
#if PLATFORM_LINUX
|
|
TEST_F(PathTest, EnsureEndsWithPathSeparator) {
|
|
std::string path = "/data";
|
|
path::EnsureEndsWithPathSeparator(&path);
|
|
EXPECT_EQ(path, "/data/");
|
|
path::EnsureEndsWithPathSeparator(&path);
|
|
EXPECT_EQ(path, "/data/");
|
|
|
|
path = "C:\\data\\";
|
|
path::EnsureEndsWithPathSeparator(&path);
|
|
EXPECT_EQ(path, "C:\\data\\");
|
|
}
|
|
#endif
|
|
|
|
TEST_F(PathTest, EnsureDoesNotEndWithPathSeparator) {
|
|
std::string path = "C:\\data\\\\";
|
|
path::EnsureDoesNotEndWithPathSeparator(&path);
|
|
EXPECT_EQ(path, "C:\\data");
|
|
path = "C:\\data\\";
|
|
path::EnsureDoesNotEndWithPathSeparator(&path);
|
|
EXPECT_EQ(path, "C:\\data");
|
|
path::EnsureDoesNotEndWithPathSeparator(&path);
|
|
EXPECT_EQ(path, "C:\\data");
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS
|
|
TEST_F(PathTest, FixPathSeparators) {
|
|
std::string path = "dir1/dir2/file";
|
|
path::FixPathSeparators(&path);
|
|
EXPECT_EQ(path, "dir1\\dir2\\file");
|
|
}
|
|
#endif
|
|
|
|
#if PLATFORM_LINUX
|
|
TEST_F(PathTest, FixPathSeparators) {
|
|
std::string path = "dir1\\dir2\\file";
|
|
path::FixPathSeparators(&path);
|
|
EXPECT_EQ(path, "dir1/dir2/file");
|
|
}
|
|
#endif
|
|
|
|
TEST_F(PathTest, ToUnix) {
|
|
EXPECT_EQ(path::ToUnix(""), "");
|
|
EXPECT_EQ(path::ToUnix("/"), "/");
|
|
EXPECT_EQ(path::ToUnix("/foo/"), "/foo/");
|
|
EXPECT_EQ(path::ToUnix("\\"), "/");
|
|
EXPECT_EQ(path::ToUnix("\\foo"), "/foo");
|
|
EXPECT_EQ(path::ToUnix("\\foo\\"), "/foo/");
|
|
EXPECT_EQ(path::ToUnix("\\\\foo\\bar"), "//foo/bar");
|
|
EXPECT_EQ(path::ToUnix("C:\\Windows\\"), "C:/Windows/");
|
|
EXPECT_EQ(path::ToUnix("C:\\foo/bar"), "C:/foo/bar");
|
|
EXPECT_EQ(path::ToUnix("C:/foo\\bar"), "C:/foo/bar");
|
|
}
|
|
|
|
#if PLATFORM_LINUX
|
|
TEST_F(PathTest, ToNative) {
|
|
EXPECT_EQ(path::ToNative(""), "");
|
|
EXPECT_EQ(path::ToNative("/"), "/");
|
|
EXPECT_EQ(path::ToNative("/foo/"), "/foo/");
|
|
EXPECT_EQ(path::ToNative("\\"), "/");
|
|
EXPECT_EQ(path::ToNative("\\foo"), "/foo");
|
|
EXPECT_EQ(path::ToNative("\\foo\\"), "/foo/");
|
|
EXPECT_EQ(path::ToNative("\\\\foo\\bar"), "//foo/bar");
|
|
EXPECT_EQ(path::ToNative("C:\\Windows\\"), "C:/Windows/");
|
|
EXPECT_EQ(path::ToNative("C:\\foo/bar"), "C:/foo/bar");
|
|
EXPECT_EQ(path::ToNative("C:/foo\\bar"), "C:/foo/bar");
|
|
}
|
|
#endif
|
|
|
|
#if PLATFORM_WINDOWS
|
|
TEST_F(PathTest, ToNative) {
|
|
EXPECT_EQ(path::ToNative(""), "");
|
|
EXPECT_EQ(path::ToNative("/"), "\\");
|
|
EXPECT_EQ(path::ToNative("/foo/"), "\\foo\\");
|
|
EXPECT_EQ(path::ToNative("\\"), "\\");
|
|
EXPECT_EQ(path::ToNative("\\foo"), "\\foo");
|
|
EXPECT_EQ(path::ToNative("\\foo\\"), "\\foo\\");
|
|
EXPECT_EQ(path::ToNative("\\\\foo\\bar"), "\\\\foo\\bar");
|
|
EXPECT_EQ(path::ToNative("C:/Windows/"), "C:\\Windows\\");
|
|
EXPECT_EQ(path::ToNative("C:\\foo/bar"), "C:\\foo\\bar");
|
|
EXPECT_EQ(path::ToNative("C:/foo\\bar"), "C:\\foo\\bar");
|
|
}
|
|
#endif
|
|
|
|
TEST_F(PathTest, OpenFile) {
|
|
absl::StatusOr<FILE*> file = path::OpenFile(test_filepath_, "r");
|
|
ASSERT_OK(file);
|
|
EXPECT_TRUE(*file);
|
|
char line[256] = {0};
|
|
EXPECT_TRUE(fgets(line, sizeof(line) - 1, *file));
|
|
EXPECT_STREQ(line, kTestText);
|
|
fclose(*file);
|
|
}
|
|
|
|
TEST_F(PathTest, OpenFileFails) {
|
|
absl::StatusOr<FILE*> status = path::OpenFile("does_not_exist", "r");
|
|
EXPECT_TRUE(absl::IsNotFound(status.status()));
|
|
EXPECT_TRUE(absl::StrContains(status.status().message(),
|
|
"Failed to open file 'does_not_exist':"));
|
|
|
|
status = path::OpenFile("", "r");
|
|
EXPECT_TRUE(
|
|
absl::StrContains(status.status().message(), "Failed to open file '':"));
|
|
}
|
|
|
|
TEST_F(PathTest, OpenFile_Unicode) {
|
|
absl::StatusOr<FILE*> file = path::OpenFile(unicode_test_filepath_, "r");
|
|
ASSERT_OK(file);
|
|
EXPECT_TRUE(*file);
|
|
fclose(*file);
|
|
}
|
|
|
|
void PathTest::CheckStreamReadFileContents(
|
|
size_t buffer_size, std::vector<std::string> expected_data) const {
|
|
int index = 0;
|
|
auto handler = [&expected_data, &index](const void* data, size_t data_size) {
|
|
// ASSERTs would be better here, but they change the return type.
|
|
EXPECT_LT(index, expected_data.size());
|
|
if (static_cast<size_t>(index) >= expected_data.size()) {
|
|
return MakeStatus("Bad index");
|
|
}
|
|
|
|
EXPECT_EQ(data_size, expected_data[index].size());
|
|
if (data_size != expected_data[index].size()) {
|
|
return MakeStatus("Bad data_size");
|
|
}
|
|
EXPECT_EQ(memcmp(data, expected_data[index].data(), data_size), 0);
|
|
index++;
|
|
return absl::OkStatus();
|
|
};
|
|
|
|
absl::StatusOr<FILE*> file = path::OpenFile(test_filepath_, "rb");
|
|
ASSERT_OK(file);
|
|
EXPECT_OK(path::StreamReadFileContents(*file, buffer_size, handler));
|
|
fclose(*file);
|
|
}
|
|
|
|
std::vector<PathTest::File> PathTest::SearchFiles(const std::string& pattern,
|
|
bool recursive) {
|
|
std::vector<File> files;
|
|
|
|
auto handler = [&files](const std::string& dir, const std::string& filename,
|
|
int64_t modified_time, uint64_t size,
|
|
bool is_directory) {
|
|
files.push_back({dir, filename, is_directory});
|
|
if (is_directory) {
|
|
EXPECT_EQ(size, 0);
|
|
} else {
|
|
EXPECT_GT(size, 0);
|
|
}
|
|
EXPECT_GT(modified_time, 0);
|
|
return absl::OkStatus();
|
|
};
|
|
|
|
EXPECT_OK(path::SearchFiles(pattern, recursive, handler));
|
|
|
|
// Linux and Windows yield a slightly different order, so sort first.
|
|
std::sort(files.begin(), files.end(), [](const File& a, const File& b) {
|
|
return a.filename_ < b.filename_;
|
|
});
|
|
|
|
return files;
|
|
}
|
|
|
|
TEST_F(PathTest, SearchFiles_NonRecursiveTrailingSeparator) {
|
|
std::vector<File> files = SearchFiles(search_dir_, false);
|
|
#if PLATFORM_WINDOWS
|
|
// Windows does not list elements in a directory unless they are globbed.
|
|
EXPECT_TRUE(files.empty());
|
|
#else
|
|
// We get all elements within the search_dir_, but not beyond.
|
|
ASSERT_EQ(files.size(), 3);
|
|
|
|
EXPECT_EQ(files[0].dir_, search_dir_);
|
|
EXPECT_EQ(files[0].Path(), subdir_fulldirpath_);
|
|
EXPECT_TRUE(files[0].is_directory_);
|
|
|
|
EXPECT_EQ(files[1].dir_, search_dir_);
|
|
EXPECT_EQ(files[1].Path(), test_filepath_);
|
|
EXPECT_FALSE(files[1].is_directory_);
|
|
|
|
EXPECT_EQ(files[2].dir_, search_dir_);
|
|
EXPECT_EQ(files[2].Path(), unicode_test_filepath_);
|
|
EXPECT_FALSE(files[2].is_directory_);
|
|
#endif
|
|
}
|
|
|
|
TEST_F(PathTest, SearchFiles_NonRecursiveNoTrailingSeparator) {
|
|
std::string search_dir(search_dir_.substr(0, search_dir_.size() - 1));
|
|
std::vector<File> files = SearchFiles(search_dir, false);
|
|
#if PLATFORM_WINDOWS
|
|
// Windows does not list elements in a directory unless they are globbed.
|
|
EXPECT_TRUE(files.empty());
|
|
#else
|
|
// We get all elements within the search_dir_, but not beyond.
|
|
ASSERT_EQ(files.size(), 3);
|
|
|
|
EXPECT_EQ(files[0].dir_, search_dir_);
|
|
EXPECT_EQ(files[0].Path(), subdir_fulldirpath_);
|
|
EXPECT_TRUE(files[0].is_directory_);
|
|
|
|
EXPECT_EQ(files[1].dir_, search_dir_);
|
|
EXPECT_EQ(files[1].Path(), test_filepath_);
|
|
EXPECT_FALSE(files[1].is_directory_);
|
|
|
|
EXPECT_EQ(files[2].dir_, search_dir_);
|
|
EXPECT_EQ(files[2].Path(), unicode_test_filepath_);
|
|
EXPECT_FALSE(files[2].is_directory_);
|
|
#endif
|
|
}
|
|
|
|
TEST_F(PathTest, SearchFiles_RecursiveTrailingSeparator) {
|
|
std::vector<File> files = SearchFiles(search_dir_, true);
|
|
|
|
ASSERT_EQ(files.size(), 4);
|
|
|
|
EXPECT_EQ(files[0].dir_, search_dir_);
|
|
EXPECT_EQ(files[0].Path(), subdir_fulldirpath_);
|
|
EXPECT_TRUE(files[0].is_directory_);
|
|
|
|
EXPECT_EQ(files[1].dir_, subdir_);
|
|
EXPECT_EQ(files[1].Path(), subdir_filepath_);
|
|
EXPECT_FALSE(files[1].is_directory_);
|
|
|
|
EXPECT_EQ(files[2].dir_, search_dir_);
|
|
EXPECT_EQ(files[2].Path(), test_filepath_);
|
|
EXPECT_FALSE(files[2].is_directory_);
|
|
|
|
EXPECT_EQ(files[3].dir_, search_dir_);
|
|
EXPECT_EQ(files[3].Path(), unicode_test_filepath_);
|
|
EXPECT_FALSE(files[3].is_directory_);
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS
|
|
// Windows behaves differently when the search path does not end with a
|
|
// directory separator.
|
|
TEST_F(PathTest, SearchFiles_RecursiveNoTrailingSeparator) {
|
|
std::string search_dir(search_dir_.substr(0, search_dir_.size() - 1));
|
|
std::vector<File> files = SearchFiles(search_dir, true);
|
|
|
|
ASSERT_EQ(files.size(), 5);
|
|
|
|
EXPECT_EQ(files[0].dir_, base_dir_);
|
|
EXPECT_EQ(files[0].Path(), search_dir);
|
|
EXPECT_TRUE(files[0].is_directory_);
|
|
|
|
EXPECT_EQ(files[1].dir_, search_dir_);
|
|
EXPECT_EQ(files[1].Path(), subdir_fulldirpath_);
|
|
EXPECT_TRUE(files[1].is_directory_);
|
|
|
|
EXPECT_EQ(files[2].dir_, subdir_);
|
|
EXPECT_EQ(files[2].Path(), subdir_filepath_);
|
|
EXPECT_FALSE(files[2].is_directory_);
|
|
|
|
EXPECT_EQ(files[3].dir_, search_dir_);
|
|
EXPECT_EQ(files[3].Path(), test_filepath_);
|
|
|
|
EXPECT_EQ(files[4].dir_, search_dir_);
|
|
EXPECT_EQ(files[4].Path(), unicode_test_filepath_);
|
|
}
|
|
#endif
|
|
|
|
#if PLATFORM_WINDOWS
|
|
// Globbing is only supported on Windows.
|
|
TEST_F(PathTest, SearchFiles_NonRecursiveStar) {
|
|
std::string search_dir(search_dir_ + "*");
|
|
std::vector<File> files = SearchFiles(search_dir, false);
|
|
|
|
ASSERT_EQ(files.size(), 3);
|
|
|
|
EXPECT_EQ(files[0].dir_, search_dir_);
|
|
EXPECT_EQ(files[0].Path(), subdir_fulldirpath_);
|
|
EXPECT_TRUE(files[0].is_directory_);
|
|
|
|
EXPECT_EQ(files[1].dir_, search_dir_);
|
|
EXPECT_EQ(files[1].Path(), test_filepath_);
|
|
|
|
EXPECT_EQ(files[2].dir_, search_dir_);
|
|
EXPECT_EQ(files[2].Path(), unicode_test_filepath_);
|
|
}
|
|
#endif
|
|
|
|
#if PLATFORM_WINDOWS
|
|
// Globbing is only supported on Windows.
|
|
TEST_F(PathTest, SearchFiles_RecursiveStar) {
|
|
std::string search_dir(search_dir_ + "*");
|
|
std::vector<File> files = SearchFiles(search_dir, true);
|
|
|
|
ASSERT_EQ(files.size(), 4);
|
|
|
|
EXPECT_EQ(files[0].dir_, search_dir_);
|
|
EXPECT_EQ(files[0].Path(), subdir_fulldirpath_);
|
|
EXPECT_TRUE(files[0].is_directory_);
|
|
|
|
EXPECT_EQ(files[1].dir_, subdir_);
|
|
EXPECT_EQ(files[1].Path(), subdir_filepath_);
|
|
EXPECT_FALSE(files[1].is_directory_);
|
|
|
|
EXPECT_EQ(files[2].dir_, search_dir_);
|
|
EXPECT_EQ(files[2].Path(), test_filepath_);
|
|
|
|
EXPECT_EQ(files[3].dir_, search_dir_);
|
|
EXPECT_EQ(files[3].Path(), unicode_test_filepath_);
|
|
}
|
|
#endif
|
|
|
|
#if PLATFORM_WINDOWS
|
|
// Globbing is only supported on Windows.
|
|
TEST_F(PathTest, SearchFiles_Pattern) {
|
|
const std::string pattern = path::Join(search_dir_, "*t.t*");
|
|
std::vector<File> files = SearchFiles(pattern, false);
|
|
|
|
ASSERT_EQ(files.size(), 1);
|
|
|
|
EXPECT_EQ(files[0].dir_, search_dir_);
|
|
EXPECT_EQ(files[0].Path(), test_filepath_);
|
|
}
|
|
#endif
|
|
|
|
#if PLATFORM_WINDOWS
|
|
// Globbing is only supported on Windows.
|
|
TEST_F(PathTest, SearchFiles_PatternRecursive) {
|
|
const std::string pattern = path::Join(search_dir_, "*file*");
|
|
std::vector<File> files = SearchFiles(pattern, true);
|
|
|
|
ASSERT_EQ(files.size(), 1);
|
|
|
|
EXPECT_EQ(files[0].dir_, subdir_);
|
|
EXPECT_EQ(files[0].Path(), subdir_filepath_);
|
|
}
|
|
#endif
|
|
|
|
TEST_F(PathTest, SearchFiles_LastPartIsFile) {
|
|
// If "something" is a file in path\to\something, return that file.
|
|
std::vector<File> files = SearchFiles(unicode_test_filepath_, false);
|
|
|
|
ASSERT_EQ(files.size(), 1);
|
|
|
|
EXPECT_EQ(files[0].dir_, search_dir_);
|
|
EXPECT_EQ(files[0].Path(), unicode_test_filepath_);
|
|
}
|
|
|
|
TEST_F(PathTest, SearchFiles_LastPartIsDir) {
|
|
// If "something" is a dir in path\to\something, return nothing for a
|
|
// non-recursive case.
|
|
std::string dir = subdir_;
|
|
path::EnsureDoesNotEndWithPathSeparator(&dir);
|
|
std::vector<File> files = SearchFiles(dir, false);
|
|
#if PLATFORM_WINDOWS
|
|
// Windows does not list elements in a directory unless they are globbed.
|
|
EXPECT_TRUE(files.empty());
|
|
#else
|
|
// We get the single element within the subdir_..
|
|
ASSERT_EQ(files.size(), 1);
|
|
|
|
std::string got_dir = files[0].dir_;
|
|
path::EnsureDoesNotEndWithPathSeparator(&got_dir);
|
|
EXPECT_EQ(got_dir, subdir_fulldirpath_);
|
|
EXPECT_EQ(files[0].Path(), subdir_filepath_);
|
|
#endif
|
|
}
|
|
|
|
TEST_F(PathTest, SearchFiles_LastPartIsDir_Recursive) {
|
|
std::string dir = search_dir_;
|
|
path::EnsureDoesNotEndWithPathSeparator(&dir);
|
|
std::vector<File> files = SearchFiles(dir, true);
|
|
|
|
#if PLATFORM_WINDOWS
|
|
// 2 folders: search and subdir + 3 files: test.txt, u8"unicode test 🥤.txt,
|
|
// and subdir\\subdir_file.txt
|
|
ASSERT_EQ(files.size(), 5);
|
|
#else
|
|
// 1 folder: subdir + 3 files: test.txt, u8"unicode test 🥤.txt,
|
|
// and subdir\\subdir_file.txt
|
|
ASSERT_EQ(files.size(), 4);
|
|
#endif
|
|
}
|
|
|
|
TEST_F(PathTest, SearchFiles_HandlerFails) {
|
|
auto handler = [](const std::string&, const std::string&, int64_t, uint64_t,
|
|
bool) { return MakeStatus("bad"); };
|
|
EXPECT_NOT_OK(path::SearchFiles(base_dir_, true, handler));
|
|
}
|
|
|
|
TEST_F(PathTest, StreamReadFileContents_PiecewiseRead) {
|
|
// Check different buffer sizes.
|
|
CheckStreamReadFileContents(2, {"te", "st", " t", "ex", "t", ""});
|
|
CheckStreamReadFileContents(3, {"tes", "t t", "ext", ""});
|
|
CheckStreamReadFileContents(9, {kTestText, ""});
|
|
CheckStreamReadFileContents(10, {kTestText, ""});
|
|
CheckStreamReadFileContents(65536, {kTestText, ""});
|
|
}
|
|
|
|
TEST_F(PathTest, StreamReadFileContents_HandlerFails) {
|
|
auto handler = [](const void*, size_t) { return MakeStatus("invalid"); };
|
|
absl::StatusOr<FILE*> file = path::OpenFile(test_filepath_, "rb");
|
|
ASSERT_OK(file);
|
|
EXPECT_NOT_OK(
|
|
path::StreamReadFileContents(*file, /*buffer_size=*/32769, handler));
|
|
fclose(*file);
|
|
}
|
|
|
|
TEST_F(PathTest, StreamReadFileContents_EndOfFileIndicatorIsBuffered) {
|
|
absl::StatusOr<FILE*> file = path::OpenFile(test_filepath_, "rb");
|
|
ASSERT_OK(file);
|
|
auto handler = [file = *file](const void* data, size_t) {
|
|
if (!data) {
|
|
return absl::OkStatus();
|
|
}
|
|
// This operation resets feof(). It tests whether the calling code properly
|
|
// buffers feof() before calling the handler.
|
|
if (!feof(file)) {
|
|
return MakeStatus("feof() not set");
|
|
}
|
|
int pos = ftell(file);
|
|
fseek(file, 0, SEEK_SET);
|
|
char c;
|
|
fread(&c, 1, 1, file);
|
|
fseek(file, pos, SEEK_SET);
|
|
if (feof(file)) {
|
|
return MakeStatus("feof() set");
|
|
}
|
|
return absl::OkStatus();
|
|
};
|
|
EXPECT_OK(
|
|
path::StreamReadFileContents(*file, /*buffer_size=*/32769, handler));
|
|
fclose(*file);
|
|
}
|
|
|
|
TEST_F(PathTest, StreamWriteFileContents_PiecewiseWrite) {
|
|
// Create handler that writes |to_write| byte by byte.
|
|
std::string to_write = "test abc";
|
|
size_t index = 0;
|
|
auto handler = [&index, &to_write](const void** data, size_t* size) {
|
|
const bool done = index >= to_write.size();
|
|
*data = !done ? &to_write[index] : nullptr;
|
|
*size = !done ? 1 : 0;
|
|
index++;
|
|
return absl::OkStatus();
|
|
};
|
|
|
|
// Write to a temporary file.
|
|
std::FILE* file = std::tmpfile();
|
|
ASSERT_TRUE(file != nullptr);
|
|
EXPECT_OK(path::StreamWriteFileContents(file, handler));
|
|
|
|
// Verify contents.
|
|
rewind(file);
|
|
char line[256] = {0};
|
|
EXPECT_TRUE(fgets(line, sizeof(line) - 1, file));
|
|
EXPECT_STREQ(line, "test abc");
|
|
fclose(file);
|
|
}
|
|
|
|
TEST_F(PathTest, StreamWriteFileContents_HandlerFails) {
|
|
auto handler = [](const void**, size_t*) { return MakeStatus("invalid"); };
|
|
std::FILE* file = std::tmpfile();
|
|
ASSERT_TRUE(file != nullptr);
|
|
EXPECT_NOT_OK(path::StreamWriteFileContents(file, handler));
|
|
fclose(file);
|
|
}
|
|
|
|
TEST_F(PathTest, ReadFile_Success) {
|
|
Buffer data;
|
|
EXPECT_OK(path::ReadFile(test_filepath_, &data));
|
|
Buffer want = {'t', 'e', 's', 't', ' ', 't', 'e', 'x', 't'};
|
|
EXPECT_EQ(data, want);
|
|
}
|
|
|
|
TEST_F(PathTest, ReadFile_DoesNotExist) {
|
|
Buffer data;
|
|
absl::Status status = path::ReadFile("does_not_exist", &data);
|
|
EXPECT_TRUE(absl::IsNotFound(status));
|
|
}
|
|
|
|
TEST_F(PathTest, ReadWholeFileWithOffsetLen_Success) {
|
|
uint8_t data[9];
|
|
absl::StatusOr<size_t> bytes_read =
|
|
path::ReadFile(test_filepath_, data, 0, 9);
|
|
ASSERT_OK(bytes_read);
|
|
ASSERT_EQ(*bytes_read, 9);
|
|
std::vector<uint8_t> want = {'t', 'e', 's', 't', ' ', 't', 'e', 'x', 't'};
|
|
EXPECT_TRUE(std::equal(std::begin(data), std::end(data), std::begin(want)));
|
|
}
|
|
|
|
TEST_F(PathTest, ReadFilePartWithOffsetLen_Success) {
|
|
uint8_t data[3];
|
|
absl::StatusOr<size_t> bytes_read =
|
|
path::ReadFile(test_filepath_, data, 3, 3);
|
|
ASSERT_OK(bytes_read);
|
|
ASSERT_EQ(*bytes_read, 3);
|
|
std::vector<uint8_t> want = {'t', ' ', 't'};
|
|
EXPECT_TRUE(std::equal(std::begin(data), std::end(data), std::begin(want)));
|
|
}
|
|
|
|
TEST_F(PathTest, ReadFileWithZeroLen_Success) {
|
|
uint8_t data[9];
|
|
absl::StatusOr<size_t> bytes_read =
|
|
path::ReadFile(test_filepath_, data, 0, 0);
|
|
ASSERT_OK(bytes_read);
|
|
EXPECT_EQ(bytes_read.value(), 0);
|
|
}
|
|
|
|
TEST_F(PathTest, ReadFileWithWrongOffset_Success) {
|
|
uint8_t data[9];
|
|
absl::StatusOr<size_t> bytes_read =
|
|
path::ReadFile(test_filepath_, data, 20, 5);
|
|
ASSERT_OK(bytes_read);
|
|
ASSERT_EQ(*bytes_read, 0);
|
|
}
|
|
|
|
TEST_F(PathTest, ReadFileWithLargeLen_Success) {
|
|
uint8_t data[9];
|
|
absl::StatusOr<size_t> bytes_read =
|
|
path::ReadFile(test_filepath_, data, 0, 40);
|
|
ASSERT_OK(bytes_read);
|
|
ASSERT_EQ(*bytes_read, 9);
|
|
std::vector<uint8_t> want = {'t', 'e', 's', 't', ' ', 't', 'e', 'x', 't'};
|
|
EXPECT_TRUE(std::equal(std::begin(data), std::end(data), std::begin(want)));
|
|
}
|
|
|
|
TEST_F(PathTest, ReadFileWithOffsetLen_DoesNotExist) {
|
|
uint8_t data[9];
|
|
absl::StatusOr<size_t> bytes_read =
|
|
path::ReadFile("does_not_exist", data, 0, 5);
|
|
EXPECT_TRUE(absl::IsNotFound(bytes_read.status()));
|
|
}
|
|
|
|
TEST_F(PathTest, ReadFileString_Success) {
|
|
absl::StatusOr<std::string> data = path::ReadFile(test_filepath_);
|
|
ASSERT_OK(data);
|
|
EXPECT_EQ(*data, "test text");
|
|
}
|
|
|
|
TEST_F(PathTest, ReadFileString_DoesNotExist) {
|
|
EXPECT_ERROR(NotFound, path::ReadFile("does_not_exist"));
|
|
}
|
|
|
|
TEST_F(PathTest, ReadAllLines_DoesNotExist) {
|
|
std::vector<std::string> lines;
|
|
absl::Status status =
|
|
path::ReadAllLines("does_not_exist", &lines, path::ReadFlags::kNone);
|
|
EXPECT_TRUE(absl::IsNotFound(status));
|
|
}
|
|
|
|
TEST_F(PathTest, ReadAllLines_Success) {
|
|
EXPECT_NO_FATAL_FAILURE(CheckReadAllLines(
|
|
path::ReadFlags::kNone, {"", "line", "trailing whitespace \t", "", "",
|
|
" \t leading\twhitespace", ""}));
|
|
|
|
EXPECT_NO_FATAL_FAILURE(CheckReadAllLines(
|
|
path::ReadFlags::kRemoveEmpty,
|
|
{"line", "trailing whitespace \t", " \t leading\twhitespace"}));
|
|
|
|
EXPECT_NO_FATAL_FAILURE(CheckReadAllLines(
|
|
path::ReadFlags::kTrimWhitespace,
|
|
{"", "line", "trailing whitespace", "", "", "leading\twhitespace", ""}));
|
|
|
|
EXPECT_NO_FATAL_FAILURE(CheckReadAllLines(
|
|
path::ReadFlags::kRemoveEmpty | path::ReadFlags::kTrimWhitespace,
|
|
{"line", "trailing whitespace", "leading\twhitespace"}));
|
|
}
|
|
|
|
TEST_F(PathTest, WriteFile_Success) {
|
|
const std::string& path = tmp_path_1_;
|
|
Buffer want = {'1', '2', '3'};
|
|
EXPECT_OK(path::WriteFile(path, want));
|
|
Buffer data;
|
|
EXPECT_OK(path::ReadFile(path, &data));
|
|
EXPECT_EQ(data, want);
|
|
}
|
|
|
|
TEST_F(PathTest, WriteFile_PermissionDenied) {
|
|
const std::string& path = tmp_path_1_;
|
|
Buffer want = {'1', '2', '3'};
|
|
EXPECT_OK(path::WriteFile(path, want));
|
|
|
|
// Check if overwrite works.
|
|
EXPECT_OK(path::WriteFile(path, want));
|
|
|
|
// Make it read-only and try to write again.
|
|
RemoveWritePermission(path);
|
|
absl::Status status = path::WriteFile(path, want);
|
|
EXPECT_NOT_OK(status);
|
|
EXPECT_TRUE(absl::StrContains(status.message(), "Permission denied"));
|
|
}
|
|
|
|
// Creating symlinks require administrator permissions
|
|
#ifdef ENABLE_TESTS_REQUIRING_ADMIN_PERMISSIONS
|
|
TEST_F(PathTest, Symlink) {
|
|
std::string file_link = path::Join(base_dir_, u8"file_linkÜäЇє");
|
|
std::string target = u8"some_targetŨŴώ";
|
|
ASSERT_OK(path::CreateSymlink(target, file_link, false));
|
|
absl::StatusOr<std::string> symlink_target =
|
|
path::GetSymlinkTarget(file_link);
|
|
ASSERT_TRUE(symlink_target.ok());
|
|
ASSERT_EQ(symlink_target.value(), target);
|
|
|
|
std::string dir_link = path::Join(base_dir_, u8"dir_linkЂЄϸ");
|
|
target = u8"some_dir_ϴϭ Ⱦ_target";
|
|
ASSERT_OK(path::CreateSymlink(target, dir_link, true));
|
|
symlink_target = path::GetSymlinkTarget(dir_link);
|
|
ASSERT_OK(symlink_target);
|
|
ASSERT_EQ(symlink_target.value(), target);
|
|
|
|
symlink_target = path::GetSymlinkTarget("does not exist");
|
|
ASSERT_NOT_OK(symlink_target);
|
|
}
|
|
#endif // ENABLE_TESTS_REQUIRING_ADMIN_PERMISSIONS
|
|
|
|
TEST_F(PathTest, DirExists) {
|
|
EXPECT_TRUE(path::DirExists(base_dir_));
|
|
EXPECT_FALSE(path::DirExists(test_filepath_));
|
|
EXPECT_FALSE(path::DirExists("does_not_exist"));
|
|
}
|
|
|
|
TEST_F(PathTest, FileExists) {
|
|
EXPECT_FALSE(path::FileExists(base_dir_));
|
|
EXPECT_TRUE(path::FileExists(test_filepath_));
|
|
EXPECT_FALSE(path::FileExists("does_not_exist"));
|
|
}
|
|
|
|
TEST_F(PathTest, Exists) {
|
|
EXPECT_TRUE(path::Exists(base_dir_));
|
|
EXPECT_TRUE(path::Exists(test_filepath_));
|
|
EXPECT_FALSE(path::Exists("does_not_exist"));
|
|
}
|
|
|
|
TEST_F(PathTest, CreateDir) {
|
|
std::string dir = path::Join(tmp_dir_, "createdir_test");
|
|
std::string unicode_dir = path::Join(dir, u8"\U0001F964\U0001F964\U0001F964");
|
|
path::RemoveDirRec(dir).IgnoreError();
|
|
EXPECT_NOT_OK(path::CreateDir(path::Join(dir, "subdir")));
|
|
EXPECT_OK(path::CreateDir(dir));
|
|
EXPECT_OK(path::CreateDir(dir));
|
|
EXPECT_OK(path::CreateDir(unicode_dir));
|
|
EXPECT_TRUE(path::DirExists(unicode_dir));
|
|
// TODO: Uncomment once everything compiles on VS 2019.
|
|
// I assume the C++17 standard implemented in VS 2019 does report an error
|
|
// when creating a dir over an existing file, whereas VS 2017 does not.
|
|
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1164r1.pdf.
|
|
// EXPECT_NOT_OK(path::CreateDir(test_filepath_));
|
|
path::RemoveDirRec(dir).IgnoreError();
|
|
}
|
|
|
|
TEST_F(PathTest, CreateDirRec) {
|
|
std::string dir = path::Join(tmp_dir_, "createdir_test", "subdir");
|
|
std::string unicode_dir = path::Join(dir, u8"\U0001F964\U0001F964\U0001F964");
|
|
path::RemoveDirRec(dir).IgnoreError();
|
|
EXPECT_OK(path::CreateDirRec(dir));
|
|
EXPECT_OK(path::CreateDirRec(dir));
|
|
EXPECT_OK(path::CreateDirRec(unicode_dir));
|
|
EXPECT_TRUE(path::DirExists(unicode_dir));
|
|
// TODO: Uncomment once everything compiles on VS 2019. See above.
|
|
// EXPECT_NOT_OK(path::CreateDirRec(test_filepath_));
|
|
path::RemoveDirRec(dir).IgnoreError();
|
|
}
|
|
|
|
TEST_F(PathTest, RenameFile_Success) {
|
|
EXPECT_OK(path::WriteFile(tmp_path_1_, "x"));
|
|
EXPECT_TRUE(path::Exists(tmp_path_1_));
|
|
EXPECT_FALSE(path::Exists(tmp_path_2_));
|
|
|
|
EXPECT_OK(path::RenameFile(tmp_path_1_, tmp_path_2_));
|
|
|
|
EXPECT_FALSE(path::Exists(tmp_path_1_));
|
|
EXPECT_TRUE(path::Exists(tmp_path_2_));
|
|
}
|
|
|
|
TEST_F(PathTest, RenameFile_DoesNotExist) {
|
|
absl::Status status = path::RenameFile(tmp_path_1_, tmp_path_2_);
|
|
EXPECT_NOT_OK(status);
|
|
EXPECT_TRUE(absl::StrContains(status.message(), "No such file"));
|
|
}
|
|
|
|
TEST_F(PathTest, RenameFile_Exists) {
|
|
EXPECT_OK(path::WriteFile(tmp_path_1_, "x"));
|
|
EXPECT_OK(path::WriteFile(tmp_path_2_, "y"));
|
|
|
|
absl::Status status = path::RenameFile(tmp_path_1_, tmp_path_2_);
|
|
#if PLATFORM_WINDOWS
|
|
// Windows returns an error if the target exists.
|
|
EXPECT_NOT_OK(status);
|
|
EXPECT_TRUE(absl::StrContains(status.message(), "File exists"));
|
|
#else
|
|
// Linux atomically replaces the target with the existing source file.
|
|
EXPECT_OK(status);
|
|
#endif
|
|
}
|
|
|
|
TEST_F(PathTest, CopyFileRec_Success) {
|
|
const std::string source_path_1_ = path::Join(base_dir_, kUnicodeText);
|
|
EXPECT_OK(path::WriteFile(source_path_1_, "x"));
|
|
EXPECT_TRUE(path::Exists(source_path_1_));
|
|
EXPECT_FALSE(path::Exists(tmp_path_2_));
|
|
|
|
EXPECT_OK(path::CopyFileRec(source_path_1_, tmp_path_2_));
|
|
|
|
EXPECT_TRUE(path::Exists(source_path_1_));
|
|
EXPECT_TRUE(path::Exists(tmp_path_2_));
|
|
}
|
|
|
|
TEST_F(PathTest, CopyFileRec_DoesNotExist) {
|
|
EXPECT_ERROR(Internal, path::CopyFileRec(tmp_path_1_, tmp_path_2_));
|
|
}
|
|
|
|
TEST_F(PathTest, CopyFileRec_Exists) {
|
|
EXPECT_OK(path::WriteFile(tmp_path_1_, "x"));
|
|
EXPECT_OK(path::WriteFile(tmp_path_2_, "y"));
|
|
|
|
EXPECT_OK(path::CopyFileRec(tmp_path_1_, tmp_path_2_));
|
|
EXPECT_TRUE(path::Exists(tmp_path_1_));
|
|
EXPECT_TRUE(path::Exists(tmp_path_2_));
|
|
|
|
Buffer new_data;
|
|
EXPECT_OK(path::ReadFile(tmp_path_2_, &new_data));
|
|
EXPECT_EQ(new_data, Buffer{'x'});
|
|
}
|
|
|
|
TEST_F(PathTest, RemoveFile_Success) {
|
|
EXPECT_OK(path::WriteFile(tmp_path_1_, "x"));
|
|
EXPECT_TRUE(path::Exists(tmp_path_1_));
|
|
|
|
EXPECT_OK(path::RemoveFile(tmp_path_1_));
|
|
|
|
EXPECT_FALSE(path::Exists(tmp_path_1_));
|
|
}
|
|
|
|
TEST_F(PathTest, RemoveFile_NonExistentSuccess) {
|
|
EXPECT_FALSE(path::Exists(tmp_path_1_));
|
|
EXPECT_OK(path::RemoveFile(tmp_path_1_));
|
|
}
|
|
|
|
TEST_F(PathTest, RemoveFile_ReadOnly) {
|
|
EXPECT_OK(path::WriteFile(tmp_path_1_, "x"));
|
|
RemoveWritePermission(tmp_path_1_);
|
|
|
|
absl::Status status = path::RemoveFile(tmp_path_1_);
|
|
|
|
#if PLATFORM_WINDOWS
|
|
// Windows requires write permissions for a file to remove it.
|
|
EXPECT_NOT_OK(status);
|
|
EXPECT_TRUE(absl::StrContains(status.message(), "Permission denied"));
|
|
#else
|
|
// Linux requires write permissions to the directory to remove a file.
|
|
EXPECT_OK(status);
|
|
#endif
|
|
}
|
|
|
|
TEST_F(PathTest, GetStats_File) {
|
|
path::Stats stats;
|
|
EXPECT_OK(path::GetStats(test_filepath_, &stats));
|
|
EXPECT_EQ(stats.mode & path::MODE_IFDIR, 0);
|
|
EXPECT_NE(stats.modified_time, 0);
|
|
EXPECT_EQ(stats.size, 9);
|
|
}
|
|
|
|
TEST_F(PathTest, GetStats_UnicodeFile) {
|
|
path::Stats stats;
|
|
EXPECT_OK(path::GetStats(unicode_test_filepath_, &stats));
|
|
EXPECT_EQ(stats.mode & path::MODE_IFDIR, 0);
|
|
EXPECT_NE(stats.modified_time, 0);
|
|
EXPECT_EQ(stats.size, 12);
|
|
}
|
|
|
|
TEST_F(PathTest, GetStats_Dir) {
|
|
path::Stats stats;
|
|
EXPECT_OK(path::GetStats(base_dir_, &stats));
|
|
EXPECT_NE(stats.mode & path::MODE_IFDIR, 0);
|
|
EXPECT_NE(stats.modified_time, 0);
|
|
}
|
|
|
|
TEST_F(PathTest, GetStats_DoesNotExist) {
|
|
path::Stats stats;
|
|
absl::Status status = path::GetStats("does_not_exist", &stats);
|
|
EXPECT_TRUE(absl::IsNotFound(status));
|
|
}
|
|
|
|
TEST_F(PathTest, GetFileSize) {
|
|
uint64_t size;
|
|
EXPECT_OK(path::FileSize(test_filepath_, &size));
|
|
EXPECT_EQ(size, 9);
|
|
}
|
|
|
|
TEST_F(PathTest, ReplaceFile) {
|
|
const std::string& old_file = tmp_path_1_;
|
|
const std::string& new_file = tmp_path_2_;
|
|
|
|
EXPECT_OK(path::WriteFile(old_file, "a"));
|
|
EXPECT_OK(path::WriteFile(new_file, "b"));
|
|
|
|
// Set up a different mode. Files initially have rw permissions.
|
|
RemoveWritePermission(new_file);
|
|
path::Stats old_stats, new_stats;
|
|
EXPECT_OK(path::GetStats(old_file, &old_stats));
|
|
EXPECT_OK(path::GetStats(new_file, &new_stats));
|
|
EXPECT_NE(old_stats.mode, new_stats.mode);
|
|
|
|
EXPECT_OK(path::ReplaceFile(old_file, new_file));
|
|
|
|
Buffer old_replaced_by_new_data;
|
|
EXPECT_OK(path::ReadFile(old_file, &old_replaced_by_new_data));
|
|
EXPECT_EQ(old_replaced_by_new_data, Buffer{'b'});
|
|
|
|
// Mode should match mode from NEW file.
|
|
path::Stats old_replaced_by_new_stats;
|
|
EXPECT_OK(path::GetStats(old_file, &old_replaced_by_new_stats));
|
|
EXPECT_EQ(old_replaced_by_new_stats.mode, new_stats.mode);
|
|
}
|
|
|
|
TEST_F(PathTest, ReplaceFile_OldDoesNotExist) {
|
|
const std::string& old_file = tmp_path_1_;
|
|
const std::string& new_file = tmp_path_2_;
|
|
|
|
absl::Status status = path::ReplaceFile(old_file, new_file);
|
|
EXPECT_NOT_OK(status);
|
|
EXPECT_TRUE(absl::StrContains(status.message(), old_file));
|
|
EXPECT_TRUE(absl::StrContains(status.message(), "No such file"));
|
|
}
|
|
|
|
TEST_F(PathTest, ReplaceFile_NewDoesNotExist) {
|
|
const std::string& old_file = tmp_path_1_;
|
|
const std::string& new_file = tmp_path_2_;
|
|
|
|
EXPECT_OK(path::WriteFile(old_file, "a"));
|
|
|
|
absl::Status status = path::ReplaceFile(old_file, new_file);
|
|
EXPECT_NOT_OK(status);
|
|
EXPECT_TRUE(absl::StrContains(status.message(), old_file));
|
|
EXPECT_TRUE(absl::StrContains(status.message(), new_file));
|
|
EXPECT_TRUE(absl::StrContains(status.message(), "No such file"));
|
|
EXPECT_FALSE(path::Exists(new_file));
|
|
}
|
|
|
|
TEST_F(PathTest, ChangeMode_Success) {
|
|
const std::string& path = tmp_path_1_;
|
|
EXPECT_OK(path::WriteFile(path, "a"));
|
|
path::Stats stats;
|
|
EXPECT_OK(path::GetStats(path, &stats));
|
|
EXPECT_NE(stats.mode & path::MODE_IWUSR, 0);
|
|
EXPECT_OK(path::ChangeMode(path, stats.mode & ~path::MODE_IWUSR));
|
|
EXPECT_OK(path::GetStats(path, &stats));
|
|
EXPECT_EQ(stats.mode & path::MODE_IWUSR, 0);
|
|
}
|
|
|
|
TEST_F(PathTest, ChangeMode_DoesNotExist) {
|
|
const std::string& path = tmp_path_1_;
|
|
EXPECT_FALSE(path::Exists(path));
|
|
|
|
absl::Status status = path::ChangeMode(path, 0);
|
|
EXPECT_NOT_OK(status);
|
|
EXPECT_TRUE(absl::StrContains(status.message(), "No such file"));
|
|
}
|
|
|
|
TEST_F(PathTest, RemoveDirRec_Success) {
|
|
// Create a directory structure.
|
|
std::string dir_path = path::Join(base_dir_, "dirrec\\");
|
|
ASSERT_OK(path::CreateDirRec(dir_path));
|
|
std::string subdir_path = path::Join(dir_path, "subdirrec\\");
|
|
std::string subdir_path2 = path::Join(dir_path, "subdirrec2\\");
|
|
ASSERT_OK(path::CreateDirRec(subdir_path));
|
|
ASSERT_OK(path::CreateDirRec(subdir_path2));
|
|
std::string file_path_1 = path::Join(subdir_path, "file.txt");
|
|
std::string file_path_2 = path::Join(subdir_path2, "file.txt");
|
|
EXPECT_OK(path::WriteFile(file_path_1, "x"));
|
|
EXPECT_OK(path::WriteFile(file_path_2, "x"));
|
|
EXPECT_OK(path::RemoveDirRec(dir_path));
|
|
EXPECT_FALSE(path::Exists(dir_path));
|
|
}
|
|
|
|
TEST_F(PathTest, RemoveDirRec_Fail) {
|
|
EXPECT_OK(path::RemoveDirRec("non_existing_file"));
|
|
}
|
|
|
|
TEST_F(PathTest, PathSeparator) {
|
|
#if PLATFORM_WINDOWS
|
|
EXPECT_EQ('\\', path::PathSeparator());
|
|
EXPECT_EQ('/', path::OtherPathSeparator());
|
|
#elif PLATFORM_LINUX
|
|
EXPECT_EQ('/', path::PathSeparator());
|
|
EXPECT_EQ('\\', path::OtherPathSeparator());
|
|
#endif
|
|
}
|
|
|
|
TEST_F(PathTest, SetFileTime_FileNotFound) {
|
|
constexpr time_t kTimestamp = 1617270506;
|
|
EXPECT_NOT_OK(path::SetFileTime("non_existing_file", kTimestamp));
|
|
}
|
|
|
|
TEST_F(PathTest, SetFileTime_GetFileTime_Success) {
|
|
constexpr time_t kTimestamp = 1617270506;
|
|
EXPECT_OK(path::WriteFile(tmp_path_1_, Buffer()));
|
|
EXPECT_OK(path::SetFileTime(tmp_path_1_, kTimestamp));
|
|
time_t mtime;
|
|
EXPECT_OK(path::GetFileTime(tmp_path_1_, &mtime));
|
|
EXPECT_EQ(mtime, kTimestamp);
|
|
}
|
|
|
|
TEST_F(PathTest, GetFileTime_File) {
|
|
time_t mtime;
|
|
EXPECT_OK(path::GetFileTime(test_filepath_, &mtime));
|
|
EXPECT_NE(mtime, 0);
|
|
}
|
|
|
|
TEST_F(PathTest, GetFileTime_UnicodeFile) {
|
|
time_t mtime;
|
|
EXPECT_OK(path::GetFileTime(unicode_test_filepath_, &mtime));
|
|
EXPECT_NE(mtime, 0);
|
|
}
|
|
|
|
TEST_F(PathTest, GetFileTime_Dir) {
|
|
time_t mtime;
|
|
EXPECT_OK(path::GetFileTime(base_dir_, &mtime));
|
|
EXPECT_NE(mtime, 0);
|
|
}
|
|
|
|
TEST_F(PathTest, GetFileTime_FileNotFound) {
|
|
time_t mtime;
|
|
EXPECT_NOT_OK(path::GetFileTime("non_existing_file", &mtime));
|
|
}
|
|
|
|
TEST_F(PathTest, AreEqual) {
|
|
EXPECT_TRUE(path::AreEqual("path/to/file", "path/to/file"));
|
|
EXPECT_TRUE(path::AreEqual("path/other/../to/file", "path/to/file"));
|
|
EXPECT_TRUE(
|
|
path::AreEqual(path::Join("path/to", kUnicodeTestFileName),
|
|
path::Join("path/../path/to", kUnicodeTestFileName)));
|
|
EXPECT_TRUE(path::AreEqual(path::Join("path/to", kUnicodeTestFileName),
|
|
path::Join("path/to/.", kUnicodeTestFileName)));
|
|
EXPECT_TRUE(path::AreEqual(u8"path/\U0001F964/file",
|
|
u8"path/\U0001F964/../\U0001F964/file"));
|
|
EXPECT_TRUE(path::AreEqual(u8"path/\U0001F964/file",
|
|
u8"path/\U0001F964/../../path/\U0001F964/./file"));
|
|
#if PLATFORM_WINDOWS
|
|
// Backslash is not a valid directory separator on Linux.
|
|
EXPECT_TRUE(path::AreEqual("c:\\dir\\sub_dir\\file.txt",
|
|
"C:\\dir\\more\\..\\..\\dir\\sub_dir\\file.txt"));
|
|
EXPECT_FALSE(path::AreEqual("c:\\dir\\sub_dir\\file.txt",
|
|
"C:\\dir\\more\\..\\dir\\sub_dir\\file.txt"));
|
|
#endif
|
|
EXPECT_FALSE(path::AreEqual("path/to/file", "path/file/to"));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace cdc_ft
|