Implemented Linux equivalent to Windows file_path*

This adds a Linux equivalent to file path operations done in the Windows
headers and source files. Some differences that will occur are the
invalid path names and contents, as there are no invalid path names, and
only one invalid path content (excluding non-printable characters) on
Linux.
This commit is contained in:
Timothy Hutchins
2023-05-21 18:58:33 -05:00
parent 0d0f3dcc7e
commit 046652347f
3 changed files with 1028 additions and 0 deletions
@@ -0,0 +1,209 @@
// 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
//
// https://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 "internal/platform/implementation/linux/file_path.h"
#include <algorithm>
#include <cctype>
#include <fstream>
#include <iterator>
#include <string>
#include <vector>
#include "absl/strings/str_cat.h"
#include "internal/platform/implementation/linux/utils.h"
#include "internal/platform/implementation/linux/device_info.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
const wchar_t* kUpOneLevel = L"/..";
constexpr wchar_t kPathDelimiter = L'/';
constexpr wchar_t kReplacementChar = L'_';
constexpr wchar_t kForwardSlash = L'/';
constexpr wchar_t kBackSlash = L'\\';
std::wstring FilePath::GetCustomSavePath(std::wstring parent_folder,
std::wstring file_name) {
std::wstring path;
path += parent_folder + kPathDelimiter + file_name;
return CreateOutputFileWithRename(path);
}
std::wstring FilePath::GetDownloadPath(std::wstring parent_folder,
std::wstring file_name) {
return CreateOutputFileWithRename(
GetDownloadPathInternal(parent_folder, file_name));
}
std::wstring FilePath::GetDownloadPathInternal(std::wstring parent_folder,
std::wstring file_name) {
DeviceInfo info = DeviceInfo();
std::optional<std::filesystem::path> download_path = info.GetDownloadPath();
std::string base_path;
std::wstring wide_path(string_to_wstring(base_path));
if (!download_path) {
// If grabbing the download path fails then we make a custom one
base_path = getenv("HOME");
base_path.append("/Downloads");
}
else {
base_path = download_path.value();
}
// If parent_folder starts with a \\ or /, then strip it
while (!parent_folder.empty() && (*parent_folder.begin() == kBackSlash ||
*parent_folder.begin() == kForwardSlash)) {
parent_folder.erase(0, 1);
}
// If parent_folder ends with a \\ or /, then strip it
while (!parent_folder.empty() && (*parent_folder.rbegin() == kBackSlash ||
*parent_folder.rbegin() == kForwardSlash)) {
parent_folder.erase(parent_folder.size() - 1, 1);
}
// If file_name starts with a \\, then strip it
while (!file_name.empty() && (*file_name.begin() == kBackSlash ||
*file_name.begin() == kForwardSlash)) {
file_name.erase(0, 1);
}
// If file_name ends with a \\, then strip it
while (!file_name.empty() && (*file_name.rbegin() == kBackSlash ||
*file_name.rbegin() == kForwardSlash)) {
file_name.erase(file_name.size() - 1, 1);
}
std::wstring path;
if (parent_folder.empty()) {
path =
file_name.empty() ? wide_path : wide_path + kForwardSlash + file_name;
} else {
path = file_name.empty() ? wide_path + kForwardSlash + parent_folder
: wide_path + kForwardSlash + parent_folder +
kForwardSlash + file_name;
}
// Convert to UTF8 format.
return path;
}
// If the file already exists we add " (x)", where x is an incrementing number,
// starting at 1, using the next non-existing number, to the file name, just
// before the first dot, or at the end if no dot. The absolute path is returned.
std::wstring FilePath::CreateOutputFileWithRename(std::wstring path) {
std::wstring sanitized_path(path);
// Replace any \\ with /
std::replace(sanitized_path.begin(), sanitized_path.end(), kBackSlash,
kForwardSlash);
// Remove any /..'s
SanitizePath(sanitized_path);
auto last_delimiter = sanitized_path.find_last_of(kPathDelimiter);
std::wstring folder(sanitized_path.substr(0, last_delimiter));
std::wstring file_name(sanitized_path.substr(last_delimiter));
// Locate the last dot
auto first = file_name.find_last_of('.');
if (first == std::string::npos) {
first = file_name.size();
}
// Break the string at the dot.
auto file_name1 = file_name.substr(0, first);
auto file_name2 = file_name.substr(first);
// Construct the target file name
std::wstring target(sanitized_path);
std::fstream file;
// Open file as std::wstring
file.open(wstring_to_string(target), std::fstream::binary | std::fstream::in);
// While we successfully open the file, keep incrementing the count.
int count = 0;
while (!(file.rdstate() & std::ifstream::failbit)) {
file.close();
target = (folder + file_name1 + L" (" + std::to_wstring(++count) + L")" +
file_name2);
file.clear();
file.open(wstring_to_string(target), std::fstream::binary | std::fstream::in);
}
if (count > 0) {
NEARBY_LOGS(INFO) << "Renamed " << wstring_to_string(path) << " to "
<< wstring_to_string(target);
}
// The above leaves the file open, so close it.
file.close();
return target;
}
std::wstring FilePath::MutateForbiddenPathElements(std::wstring& str) {
// There are no forbidden paths in Linux
return str;
}
void FilePath::SanitizePath(std::wstring& path) {
size_t pos = std::wstring::npos;
// Search for the substring in string in a loop until nothing is found
while ((pos = path.find(kUpOneLevel)) != std::string::npos) {
// If found then erase it from string
path.erase(pos, wcslen(kUpOneLevel));
}
ReplaceInvalidCharacters(path);
}
// Legit the only illegal character in Linux
char kIllegalFileCharacters[] = {'/'};
void FilePath::ReplaceInvalidCharacters(std::wstring& path) {
for (auto &character : path) {
// If 0 < character < 32, it's illegal, replace it
if (character > 0 && character < 32) {
NEARBY_LOGS(INFO) << "In path " << wstring_to_string(path)
<< " replaced \'" << std::string(1, character) << "\' with \'"
<< std::string(1, kReplacementChar);
character = kReplacementChar;
}
for (auto illegal_character : kIllegalFileCharacters) {
if (character == illegal_character) {
NEARBY_LOGS(INFO) << "In path " << wstring_to_string(path)
<< " replaced \'" << std::string(1, character)
<< "\' with \'" << std::string(1, kReplacementChar);
character = kReplacementChar;
}
}
}
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,49 @@
// 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
//
// https://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.
#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_LINUX_FILE_PATH_H_
#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_LINUX_FILE_PATH_H_
#include <string>
#include "absl/strings/string_view.h"
namespace nearby {
namespace linux {
class FilePath {
public:
static std::wstring GetCustomSavePath(std::wstring parent_folder,
std::wstring file_name);
static std::wstring GetDownloadPath(std::wstring parent_folder,
std::wstring file_name);
private:
// If the file already exists we add " (x)", where x is an incrementing
// number, starting at 1, using the next non-existing number, to the
// file name, just before the first dot, or at the end if no dot. The
// absolute path is returned.
static std::wstring CreateOutputFileWithRename(std::wstring path);
static void ReplaceInvalidCharacters(std::wstring& path);
static void SanitizePath(std::wstring& path);
static std::wstring MutateForbiddenPathElements(std::wstring& str);
static std::wstring GetDownloadPathInternal(std::wstring parent_folder,
std::wstring file_name);
};
} // namespace linux
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_LINUX_FILE_PATH_H_
@@ -0,0 +1,770 @@
// 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
//
// https://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 "internal/platform/implementation/linux/file_path.h"
#include <algorithm>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include <internal/platform/implementation/linux/device_info.h>
#include <internal/platform/implementation/linux/utils.h>
namespace nearby {
namespace linux {
namespace {
const wchar_t* kFileName(L"increment_file_test.txt");
const wchar_t* kFirstIterationFileName(L"/increment_file_test (1).txt");
const wchar_t* kSecondIterationFileName(L"/increment_file_test (2).txt");
const wchar_t* kThirdIterationFileName(L"/increment_file_test (3).txt");
const wchar_t* kNoDotsFileName(L"incrementfiletesttxt");
const wchar_t* kOneIterationNoDotsFileName(L"/incrementfiletesttxt (1)");
const wchar_t* kMultipleDotsFileName(L"increment.file.test.txt");
const wchar_t* kOneIterationMultipleDotsFileName(
L"/increment.file.test (1).txt");
const wchar_t* kImmediateEscape(L"../");
const wchar_t* kLongEscapeBackSlash(L"..\\test\\..\\..\\test");
const wchar_t* kTwoLevelFolder(L"/test/test");
const wchar_t* kLongEscapeSlash(L"../test/../../test");
const wchar_t* kLongEscapeMixedSlash(L"../test\\..\\../test");
const wchar_t* kLongEscapeEndingEscape(L"../test/../../test/..");
const wchar_t* kLongEscapeEndingEscapeWithSlash(
L"../test/../../test/../../../");
} // namespace
// Can't run on google 3, I presume the SHGetKnownFolderPath
// fails.
class FilePathTests : public testing::Test {
protected:
// You can define per-test set-up logic as usual.
FilePathTests() {
default_download_path_ = string_to_wstring(DeviceInfo().GetDownloadPath().value_or(std::string(getenv("HOME")).append("/Downloads")));
}
std::wstring default_download_path_;
};
TEST_F(FilePathTests, GetDownloadPathWithEmptyStringArguments\
ShouldReturnBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_);
} // NOLINT false lint error here
TEST_F(FilePathTests, GetDownloadPathWithSlashParent\
FolderArgumentsShouldReturnBaseDownloadPath) {
std::wstring parent_folder(L"/");
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_);
} // NOLINT false lint error here
TEST_F(FilePathTests, GetDownloadPathWithBackslashParent\
FolderArgumentsShouldReturnBaseDownloadPath) {
std::wstring parent_folder(L"\\");
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_);
} // NOLINT false lint error here
TEST_F(FilePathTests, GetDownloadPathWithAttemptToEscape\
UsersDownloadFolderShouldReturnDownloadPathNotEscapingUsersDownloadFolder) {
std::wstring parent_folder(kImmediateEscape);
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_);
}
TEST_F(FilePathTests, GetDownloadPathWithMultiple\
AttemptsToEscapeUsersDownloadFolderWithBackslashShouldReturnDownloadPath\
NotEscapingUsersDownloadFolder) {
std::wstring parent_folder(kLongEscapeBackSlash);
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_ + kTwoLevelFolder);
}
TEST_F(FilePathTests, GetDownloadPathWithMultiple\
AttemptsToEscapeUsersDownloadFolderShouldReturnDownloadPathNotEscapingUsers\
DownloadFolder) {
std::wstring parent_folder(kLongEscapeSlash);
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_ + kTwoLevelFolder);
}
TEST_F(FilePathTests, GetDownloadPathWithMultiple\
AttemptsToEscapeUsersDownloadFolderWithMixedSlashShouldReturnDownloadPath\
NotEscapingUsersDownloadFolder) {
std::wstring parent_folder(kLongEscapeMixedSlash);
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_ + kTwoLevelFolder);
}
TEST_F(FilePathTests, GetDownloadPathWithMultiple\
AttemptsToEscapeUsersDownloadFolderWithEndingEscapeShouldReturnDownload\
PathNotEscapingUsersDownloadFolder) {
std::wstring parent_folder(kLongEscapeEndingEscape);
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_ + kTwoLevelFolder);
}
TEST_F(FilePathTests, GetDownloadPathWithMultiple\
AttemptsToEscapeUsersDownloadFolderWithEndingSlashShouldReturnDownloadPathNot\
EscapingUsersDownloadFolder) {
std::wstring parent_folder(kLongEscapeEndingEscapeWithSlash);
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_ + kTwoLevelFolder);
}
TEST_F(FilePathTests, GetDownloadPathWithSlashFileName\
ArgumentsShouldReturnBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"/");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_);
}
TEST_F(FilePathTests, GetDownloadPathWithBackslashFile\
NameArgumentsShouldReturnBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"\\");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
auto result_size = actual.size();
auto default_size = default_download_path_.size();
EXPECT_EQ(actual, default_download_path_);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolder\
ShouldReturnParentFolderAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"test_parent_folder");
std::wstring file_name(L"");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolder\
StartingWithSlashArgumentsShouldReturnParentFolderAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"/test_parent_folder");
std::wstring file_name(L"");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolder\
StartingWithBackslashArgumentsShouldReturnParentFolderAppendedToBase\
DownloadPath) {
std::wstring parent_folder(L"\\test_parent_folder");
std::wstring file_name(L"");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolder\
EndingWithSlashArgumentsShouldReturnParentFolderAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"test_parent_folder/");
std::wstring file_name(L"");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolder\
EndingWithBackslashArguments\
ShouldReturnParentFolderAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"test_parent_folder\\");
std::wstring file_name(L"");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithFileName\
BeginningWithSlashArgumentsShouldReturnFileNameAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"/test_file_name.name");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_file_name.name";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithFileName\
BeginningWithBackslashArgumentsShouldReturnFileNameAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"\\test_file_name.name");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_file_name.name";
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, path.str().c_str());
}
TEST_F(FilePathTests, GetDownloadPathWithFileNameEnding\
WithSlashArgumentsShouldReturnFileNameAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"test_file_name.name/");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_file_name.name";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithFileNameEnding\
WithBackslashArgumentsShouldReturnFileNameAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"test_file_name.name\\");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_file_name.name";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolderAnd\
FileNameArgumentsShould\
ReturnParentFolderAndFileNameAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"test_parent_folder");
std::wstring file_name(L"test_file_name.name");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder"
<< "/"
<< "test_file_name.name";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolder\
EndingWithBackslashAndFileNameArgumentsShouldReturnParentFolderAndFileName\
AppendedToBaseDownloadPath) {
std::wstring parent_folder(L"test_parent_folder\\");
std::wstring file_name(L"test_file_name.name");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder"
<< "/"
<< "test_file_name.name";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithFileName\
StartingWithBackslashAndParentFolderArgumentsShouldReturnParentFolderAnd\
FileNameAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"test_parent_folder");
std::wstring file_name(L"\\test_file_name.name");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder"
<< "/"
<< "test_file_name.name";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacters\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x05,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test\x5Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_LowestIllegalFileNameCharacter\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x01,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test\x1Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_HighestIllegalFileNameCharacter\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x1f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test\x1fTest");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacterQuestionMark\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test?Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacterAsterisk\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test*Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacterLessThan\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test<Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacterGreaterThan\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test>Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacterVerticalBar\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test|Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacterColon\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test:Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_FileDoesntExist\
ReturnsFileWithPassedName) {
std::wstring file_name(kFileName);
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/");
expected.append(file_name);
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_FileExistsReturns\
FileWithIncrementedName) {
std::wstring file_name(kFileName);
std::wstring renamed_file_name(kFirstIterationFileName);
std::wstring parent_folder(L"");
std::wstring output_file_path(default_download_path_);
output_file_path.append(L"/");
output_file_path.append(file_name);
std::wstring expected(default_download_path_);
expected += renamed_file_name;
std::wifstream input_file;
std::wofstream output_file;
output_file.open(wstring_to_string(output_file_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
// Remove the file and check that it is removed
// File 1
std::filesystem::remove(output_file_path.c_str());
input_file.open(wstring_to_string(output_file_path), std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
}
TEST_F(FilePathTests, GetDownloadPath_MultipleFilesExist\
ReturnsNextIncrementedFileName) {
std::ofstream output_file;
std::ifstream input_file;
std::wstring file_name(kFileName);
std::wstring first_renamed_file_name(kFirstIterationFileName);
std::wstring second_renamed_file_name(kSecondIterationFileName);
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(second_renamed_file_name.c_str());
std::wstring output_file1_path(default_download_path_);
output_file1_path.append(L"/" + file_name);
std::wstring output_file2_path(default_download_path_);
output_file2_path.append(first_renamed_file_name);
// Create the test files
output_file.open(wstring_to_string(output_file1_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
output_file.clear();
output_file.open(wstring_to_string(output_file2_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(expected, actual);
// Remove the test files and check that it is removed
// File 1
std::filesystem::remove(wstring_to_string(output_file1_path).c_str());
input_file.open(output_file1_path, std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
// File 2
std::filesystem::remove(wstring_to_string(output_file2_path).c_str());
input_file.clear();
input_file.open(wstring_to_string(output_file2_path), std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
}
TEST_F(FilePathTests, GetDownloadPath_FileNameContains\
MultipleDotsReturnsIncrementBeforeFirstDot) {
std::ifstream input_file;
std::ofstream output_file;
std::wstring file_name(kMultipleDotsFileName);
std::wstring renamed_file_name(kOneIterationMultipleDotsFileName);
std::wstring parent_folder(L"");
std::wstring output_file1_path(default_download_path_);
output_file1_path.append(L"/" + file_name);
std::wstring output_file2_path(default_download_path_);
output_file2_path.append(renamed_file_name);
std::wstring expected(default_download_path_);
expected.append(renamed_file_name);
output_file.open(wstring_to_string(output_file1_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(expected, actual);
std::filesystem::remove(wstring_to_string(output_file1_path).c_str());
input_file.open(wstring_to_string(output_file1_path), std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
}
TEST_F(FilePathTests, GetDownloadPath_FileNameContainsNo\
DotsReturnsWithIncrementAtEnd) {
std::ifstream input_file;
std::ofstream output_file;
std::wstring file_name(kNoDotsFileName);
std::wstring renamed_file_name(kOneIterationNoDotsFileName);
std::wstring parent_folder(L"");
std::wstring output_file1_path(default_download_path_);
output_file1_path.append(L"/" + file_name);
std::wstring output_file2_path(default_download_path_);
output_file2_path.append(L"/" + renamed_file_name);
std::wstring expected(default_download_path_);
expected.append(renamed_file_name);
output_file.open(wstring_to_string(output_file1_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(expected, actual);
std::filesystem::remove(wstring_to_string(output_file1_path).c_str());
input_file.open(wstring_to_string(output_file1_path), std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
}
TEST_F(FilePathTests, GetDownloadPath_FileNameExistsWith\
AHoleBetweenRenamedFiles) {
std::ifstream input_file;
std::ofstream output_file;
std::wstring file_name(kFileName);
std::wstring file_name1(kFirstIterationFileName);
std::wstring file_name2(kSecondIterationFileName);
std::wstring file_name3(kThirdIterationFileName);
std::wstring parent_folder(L"");
// Create the path for the original file name
std::wstring output_file_path(default_download_path_);
output_file_path.append(
L"/" +
file_name); // Original file name example: "increment_file_test.txt"
// Create the path for the first iteration of the original file name
std::wstring output_file1_path(default_download_path_);
output_file1_path.append(file_name1); // First iteration on original file
// name example:
// "increment_file_test (1).txt"
// Create the path for the third iteration of the original file name
std::wstring output_file3_path(default_download_path_);
output_file3_path.append(
file_name3); // Third iteration on original file
// name example: "increment_file_test (3).txt"
// Create the expected result which is the second iteration of the original
// file name
std::wstring expected(default_download_path_);
expected.append(file_name2); // Second iteration on original file name
// example: "increment_file_test (2).txt"
// Create the original file
output_file.open(wstring_to_string(output_file_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
// Create the first iteration of the original file
output_file.clear();
output_file.open(wstring_to_string(output_file1_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
// Create the third iteration of the original file
output_file.clear();
output_file.open(wstring_to_string(output_file3_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
// This should return the second iteration of the original file
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(expected, actual);
// Delete the original file
std::filesystem::remove(wstring_to_string(output_file_path).c_str());
input_file.open(wstring_to_string(output_file_path), std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
// Delete the first iteration of the original file
input_file.clear(); // Reset the input_file state
std::filesystem::remove(wstring_to_string(output_file1_path).c_str());
input_file.open(wstring_to_string(output_file1_path), std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
// Delete the third iteration of the original file
input_file.clear(); // Reset the input_file state
std::filesystem::remove(wstring_to_string(output_file3_path).c_str());
input_file.open(wstring_to_string(output_file3_path), std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
}
} // namespace linux
} // namespace nearby