blob: 321009fe5a435aeb378ef63530e7096d142f54c2 [file] [log] [blame] [edit]
/*
* Copyright (C) 2015 Canon Inc. All rights reserved.
* Copyright (C) 2017-2022 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "Test.h"
#include "Utilities.h"
#include <ranges>
#include <wtf/FileHandle.h>
#include <wtf/FileSystem.h>
#include <wtf/MainThread.h>
#include <wtf/MappedFileData.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/MakeString.h>
namespace TestWebKitAPI {
constexpr auto FileSystemTestData = "This is a test"_s;
static void createTestFile(const String& path)
{
auto written = FileSystem::overwriteEntireFile(path, byteCast<uint8_t>(FileSystemTestData.span()));
EXPECT_TRUE(written);
EXPECT_GE(*written, 0u);
}
// FIXME: Refactor FileSystemTest and FragmentedSharedBufferTest as a single class.
class FileSystemTest : public testing::Test {
public:
void SetUp() override
{
WTF::initializeMainThread();
// create temp file.
auto result = FileSystem::openTemporaryFile("tempTestFile"_s);
m_tempFilePath = result.first;
auto handle = WTF::move(result.second);
handle.write(byteCast<uint8_t>(FileSystemTestData.span()));
handle = { };
m_tempFileSymlinkPath = FileSystem::createTemporaryFile("tempTestFile-symlink"_s);
FileSystem::deleteFile(m_tempFileSymlinkPath);
FileSystem::createSymbolicLink(m_tempFilePath, m_tempFileSymlinkPath);
// Create temp directory.
m_tempEmptyFolderPath = FileSystem::createTemporaryFile("tempEmptyFolder"_s);
FileSystem::deleteFile(m_tempEmptyFolderPath);
FileSystem::makeAllDirectories(m_tempEmptyFolderPath);
m_tempEmptyFolderSymlinkPath = FileSystem::createTemporaryFile("tempEmptyFolder-symlink"_s);
FileSystem::deleteFile(m_tempEmptyFolderSymlinkPath);
FileSystem::createSymbolicLink(m_tempEmptyFolderPath, m_tempEmptyFolderSymlinkPath);
m_tempEmptyFilePath = FileSystem::createTemporaryFile("tempEmptyTestFile"_s);
m_spaceContainingFilePath = FileSystem::createTemporaryFile("temp Empty Test File"_s);
m_bangContainingFilePath = FileSystem::createTemporaryFile("temp!Empty!Test!File"_s);
m_quoteContainingFilePath = FileSystem::createTemporaryFile("temp\"Empty\"TestFile"_s);
}
void TearDown() override
{
FileSystem::deleteFile(m_tempFilePath);
FileSystem::deleteFile(m_tempFileSymlinkPath);
FileSystem::deleteNonEmptyDirectory(m_tempEmptyFolderPath);
FileSystem::deleteFile(m_tempEmptyFolderSymlinkPath);
FileSystem::deleteFile(m_tempEmptyFilePath);
FileSystem::deleteFile(m_spaceContainingFilePath);
FileSystem::deleteFile(m_bangContainingFilePath);
FileSystem::deleteFile(m_quoteContainingFilePath);
}
const String& tempFilePath() const { return m_tempFilePath; }
const String& tempFileSymlinkPath() const { return m_tempFileSymlinkPath; }
const String& tempEmptyFolderPath() const { return m_tempEmptyFolderPath; }
const String& tempEmptyFolderSymlinkPath() const { return m_tempEmptyFolderSymlinkPath; }
const String& tempEmptyFilePath() const { return m_tempEmptyFilePath; }
const String& spaceContainingFilePath() const { return m_spaceContainingFilePath; }
const String& bangContainingFilePath() const { return m_bangContainingFilePath; }
const String& quoteContainingFilePath() const { return m_quoteContainingFilePath; }
private:
String m_tempFilePath;
String m_tempFileSymlinkPath;
String m_tempEmptyFolderPath;
String m_tempEmptyFolderSymlinkPath;
String m_tempEmptyFilePath;
String m_spaceContainingFilePath;
String m_bangContainingFilePath;
String m_quoteContainingFilePath;
};
TEST_F(FileSystemTest, MappingMissingFile)
{
auto mappedFileData = FileSystem::mapFile(String("not_existing_file"_s), FileSystem::MappedFileMode::Shared);
EXPECT_FALSE(!!mappedFileData);
}
TEST_F(FileSystemTest, MappingExistingFile)
{
auto mappedFileData = FileSystem::mapFile(tempFilePath(), FileSystem::MappedFileMode::Shared);
EXPECT_TRUE(!!mappedFileData);
EXPECT_TRUE(mappedFileData->size() == strlen(FileSystemTestData));
EXPECT_TRUE(contains(FileSystemTestData.span(), mappedFileData->span()));
}
TEST_F(FileSystemTest, MappingExistingEmptyFile)
{
auto mappedFileData = FileSystem::mapFile(tempEmptyFilePath(), FileSystem::MappedFileMode::Shared);
EXPECT_TRUE(!!mappedFileData);
EXPECT_TRUE(!*mappedFileData);
}
TEST_F(FileSystemTest, FilesHaveSameVolume)
{
EXPECT_TRUE(FileSystem::filesHaveSameVolume(tempFilePath(), spaceContainingFilePath()));
EXPECT_TRUE(FileSystem::filesHaveSameVolume(spaceContainingFilePath(), bangContainingFilePath()));
EXPECT_TRUE(FileSystem::filesHaveSameVolume(bangContainingFilePath(), quoteContainingFilePath()));
}
TEST_F(FileSystemTest, fileType)
{
auto doesNotExistPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
EXPECT_FALSE(FileSystem::fileType(doesNotExistPath));
EXPECT_EQ(FileSystem::fileType(tempFilePath()), FileSystem::FileType::Regular);
EXPECT_EQ(FileSystem::fileType(tempFileSymlinkPath()), FileSystem::FileType::SymbolicLink);
EXPECT_EQ(FileSystem::fileType(tempEmptyFolderSymlinkPath()), FileSystem::FileType::SymbolicLink);
EXPECT_EQ(FileSystem::fileType(tempEmptyFolderPath()), FileSystem::FileType::Directory);
// Symlink to file symlink case.
auto symlinkToFileSymlinkPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "symlinkToSymlink"_s);
EXPECT_TRUE(FileSystem::createSymbolicLink(tempFileSymlinkPath(), symlinkToFileSymlinkPath));
EXPECT_EQ(FileSystem::fileType(symlinkToFileSymlinkPath), FileSystem::FileType::SymbolicLink);
// Symlink to directory symlink case.
auto symlinkToDirectorySymlinkPath = FileSystem::createTemporaryFile("tempTestFile-symlink"_s);
FileSystem::deleteFile(symlinkToDirectorySymlinkPath);
EXPECT_TRUE(FileSystem::createSymbolicLink(tempEmptyFolderSymlinkPath(), symlinkToDirectorySymlinkPath));
EXPECT_EQ(FileSystem::fileType(symlinkToDirectorySymlinkPath), FileSystem::FileType::SymbolicLink);
// Broken file symlink case.
EXPECT_TRUE(FileSystem::deleteFile(tempFilePath()));
EXPECT_EQ(FileSystem::fileType(tempFileSymlinkPath()), FileSystem::FileType::SymbolicLink);
// Broken directory symlink case.
EXPECT_TRUE(FileSystem::deleteNonEmptyDirectory(tempEmptyFolderPath()));
EXPECT_EQ(FileSystem::fileType(tempEmptyFolderSymlinkPath()), FileSystem::FileType::SymbolicLink);
}
#if OS(UNIX)
// FIXME: https://webkit.org/b/283603 Test crashes on Windows
TEST_F(FileSystemTest, fileTypeFollowingSymlinks)
{
auto doesNotExistPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
EXPECT_FALSE(FileSystem::fileTypeFollowingSymlinks(doesNotExistPath));
EXPECT_EQ(FileSystem::fileTypeFollowingSymlinks(tempFilePath()), FileSystem::FileType::Regular);
EXPECT_EQ(FileSystem::fileTypeFollowingSymlinks(tempFileSymlinkPath()), FileSystem::FileType::Regular);
EXPECT_EQ(FileSystem::fileTypeFollowingSymlinks(tempEmptyFolderSymlinkPath()), FileSystem::FileType::Directory);
EXPECT_EQ(FileSystem::fileTypeFollowingSymlinks(tempEmptyFolderPath()), FileSystem::FileType::Directory);
// Symlink to file symlink case.
auto symlinkToFileSymlinkPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "symlinkToSymlink"_s);
EXPECT_TRUE(FileSystem::createSymbolicLink(tempFileSymlinkPath(), symlinkToFileSymlinkPath));
EXPECT_EQ(FileSystem::fileTypeFollowingSymlinks(symlinkToFileSymlinkPath), FileSystem::FileType::Regular);
// Symlink to directory symlink case.
auto symlinkToDirectorySymlinkPath = FileSystem::createTemporaryFile("tempTestFile-symlink"_s);
FileSystem::deleteFile(symlinkToDirectorySymlinkPath);
EXPECT_TRUE(FileSystem::createSymbolicLink(tempEmptyFolderSymlinkPath(), symlinkToDirectorySymlinkPath));
EXPECT_EQ(FileSystem::fileTypeFollowingSymlinks(symlinkToDirectorySymlinkPath), FileSystem::FileType::Directory);
// Broken file symlink case.
EXPECT_TRUE(FileSystem::deleteFile(tempFilePath()));
EXPECT_FALSE(FileSystem::fileTypeFollowingSymlinks(tempFileSymlinkPath()));
// Broken directory symlink case.
EXPECT_TRUE(FileSystem::deleteNonEmptyDirectory(tempEmptyFolderPath()));
EXPECT_FALSE(FileSystem::fileTypeFollowingSymlinks(tempEmptyFolderSymlinkPath()));
}
TEST_F(FileSystemTest, isHiddenFile)
{
auto hiddenFilePath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), ".hiddenFile"_s);
EXPECT_TRUE(FileSystem::isHiddenFile(hiddenFilePath));
EXPECT_FALSE(FileSystem::isHiddenFile(tempFilePath()));
}
#endif
TEST_F(FileSystemTest, UnicodeDirectoryName)
{
String path = String::fromUTF8("/test/a\u0308lo/test.txt");
String directoryName = FileSystem::parentPath(path);
String expectedDirectoryName = String::fromUTF8("/test/a\u0308lo");
EXPECT_TRUE(expectedDirectoryName == directoryName);
}
// ===========================================================
// Tests for all the combinations for openFile, in this order:
// Level 1: ExistingFile, NonExistingFile
// Level 2: Truncate, ReadWrite, ReadOnly
// Level 3: default, FailIfFileExists
// =================== ExistingFile ==========================
// --------------------- Truncate ----------------------------
TEST_F(FileSystemTest, openExistingFileTruncate)
{
auto handle = FileSystem::openFile(tempFilePath(), FileSystem::FileOpenMode::Truncate, FileSystem::FileAccessPermission::All, { }, false);
EXPECT_TRUE(!!handle);
// Check the existing file WAS truncated when the operation succeded.
EXPECT_EQ(FileSystem::fileSize(tempFilePath()), 0);
// Write data to it and check the file size grows.
handle.write(byteCast<uint8_t>(FileSystemTestData.span()));
EXPECT_EQ(FileSystem::fileSize(tempFilePath()), strlen(FileSystemTestData));
}
TEST_F(FileSystemTest, openExistingFileTruncateFailIfFileExists)
{
auto handle = FileSystem::openFile(tempFilePath(), FileSystem::FileOpenMode::Truncate, FileSystem::FileAccessPermission::All, { }, true);
EXPECT_TRUE(!handle);
// Check the existing file wasn't truncated when the operation failed.
EXPECT_EQ(FileSystem::fileSize(tempFilePath()), strlen(FileSystemTestData));
}
// -------------------- ReadWrite ----------------------------
TEST_F(FileSystemTest, openExistingFileReadWrite)
{
auto handle = FileSystem::openFile(tempFilePath(), FileSystem::FileOpenMode::ReadWrite, FileSystem::FileAccessPermission::All, { }, false);
EXPECT_TRUE(!!handle);
// ReadWrite mode shouldn't truncate the contents of the file.
EXPECT_EQ(FileSystem::fileSize(tempFilePath()), strlen(FileSystemTestData));
// Write data to it and check the file size grows.
handle.write(byteCast<uint8_t>(FileSystemTestData.span()));
handle.write(byteCast<uint8_t>(FileSystemTestData.span()));
EXPECT_EQ(FileSystem::fileSize(tempFilePath()), strlen(FileSystemTestData) * 2);
}
TEST_F(FileSystemTest, openExistingFileReadWriteFailIfFileExists)
{
auto handle = FileSystem::openFile(tempFilePath(), FileSystem::FileOpenMode::ReadWrite, FileSystem::FileAccessPermission::All, { }, true);
EXPECT_TRUE(!handle);
// Check the existing file wasn't truncated when the operation failed.
EXPECT_EQ(FileSystem::fileSize(tempFilePath()), strlen(FileSystemTestData));
}
// --------------------- ReadOnly ----------------------------
TEST_F(FileSystemTest, openExistingFileReadOnly)
{
auto handle = FileSystem::openFile(tempFilePath(), FileSystem::FileOpenMode::Read, FileSystem::FileAccessPermission::All);
EXPECT_TRUE(!!handle);
EXPECT_EQ(FileSystem::fileSize(tempFilePath()), strlen(FileSystemTestData));
}
// ================== NonExistingFile ========================
// --------------------- Truncate ----------------------------
TEST_F(FileSystemTest, openNonExistingFileTruncate)
{
auto doesNotExistPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
EXPECT_FALSE(FileSystem::fileExists(doesNotExistPath));
auto handle = FileSystem::openFile(doesNotExistPath, FileSystem::FileOpenMode::Truncate, FileSystem::FileAccessPermission::All, { }, false);
EXPECT_TRUE(!!handle);
// The file exists at the latest by the time we request a flush (or close the handle).
handle.flush();
EXPECT_TRUE(FileSystem::fileExists(doesNotExistPath));
}
TEST_F(FileSystemTest, openNonExistingFileTruncateFailIfFileExists)
{
auto doesNotExistPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
EXPECT_FALSE(FileSystem::fileExists(doesNotExistPath));
auto handle = FileSystem::openFile(doesNotExistPath, FileSystem::FileOpenMode::Truncate, FileSystem::FileAccessPermission::All, { }, true);
EXPECT_TRUE(!!handle);
// The file exists at the latest by the time we request a flush (or close the handle).
handle.flush();
EXPECT_TRUE(FileSystem::fileExists(doesNotExistPath));
}
// -------------------- ReadWrite ----------------------------
TEST_F(FileSystemTest, openNonExistingFileReadWrite)
{
auto doesNotExistPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
EXPECT_FALSE(FileSystem::fileExists(doesNotExistPath));
auto handle = FileSystem::openFile(doesNotExistPath, FileSystem::FileOpenMode::ReadWrite, FileSystem::FileAccessPermission::All, { }, false);
EXPECT_TRUE(!!handle);
// The file exists at the latest by the time we request a flush (or close the handle).
handle.flush();
EXPECT_TRUE(FileSystem::fileExists(doesNotExistPath));
}
TEST_F(FileSystemTest, openNonExistingFileReadWriteFailIfFileExists)
{
auto doesNotExistPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
EXPECT_FALSE(FileSystem::fileExists(doesNotExistPath));
auto handle = FileSystem::openFile(doesNotExistPath, FileSystem::FileOpenMode::ReadWrite, FileSystem::FileAccessPermission::All, { }, true);
EXPECT_TRUE(!!handle);
// The file exists at the latest by the time we request a flush (or close the handle).
handle.flush();
EXPECT_TRUE(FileSystem::fileExists(doesNotExistPath));
}
// --------------------- ReadOnly ----------------------------
TEST_F(FileSystemTest, openNonExistingFileReadOnly)
{
auto doesNotExistPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
EXPECT_FALSE(FileSystem::fileExists(doesNotExistPath));
auto handle = FileSystem::openFile(doesNotExistPath, FileSystem::FileOpenMode::Read, FileSystem::FileAccessPermission::All);
EXPECT_TRUE(!handle);
}
// ===========================================================
TEST_F(FileSystemTest, deleteNonEmptyDirectory)
{
auto temporaryTestFolder = FileSystem::createTemporaryFile("deleteNonEmptyDirectoryTest"_s);
EXPECT_TRUE(FileSystem::deleteFile(temporaryTestFolder));
EXPECT_TRUE(FileSystem::makeAllDirectories(FileSystem::pathByAppendingComponents(temporaryTestFolder, std::initializer_list<StringView>({ "subfolder"_s }))));
createTestFile(FileSystem::pathByAppendingComponent(temporaryTestFolder, "file1.txt"_s));
createTestFile(FileSystem::pathByAppendingComponent(temporaryTestFolder, "file2.txt"_s));
createTestFile(FileSystem::pathByAppendingComponents(temporaryTestFolder, std::initializer_list<StringView>({ "subfolder"_s, "file3.txt"_s })));
createTestFile(FileSystem::pathByAppendingComponents(temporaryTestFolder, std::initializer_list<StringView>({ "subfolder"_s, "file4.txt"_s })));
EXPECT_FALSE(FileSystem::deleteEmptyDirectory(temporaryTestFolder));
EXPECT_TRUE(FileSystem::fileExists(temporaryTestFolder));
EXPECT_TRUE(FileSystem::deleteNonEmptyDirectory(temporaryTestFolder));
EXPECT_FALSE(FileSystem::fileExists(temporaryTestFolder));
}
TEST_F(FileSystemTest, fileExists)
{
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
EXPECT_TRUE(FileSystem::fileExists(tempFileSymlinkPath()));
EXPECT_TRUE(FileSystem::fileExists(tempEmptyFilePath()));
EXPECT_TRUE(FileSystem::fileExists(tempEmptyFolderPath()));
EXPECT_FALSE(FileSystem::fileExists(FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s)));
}
TEST_F(FileSystemTest, fileExistsBrokenSymlink)
{
auto doesNotExistPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
auto symlinkPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist-symlink"_s);
EXPECT_TRUE(FileSystem::createSymbolicLink(doesNotExistPath, symlinkPath));
EXPECT_FALSE(FileSystem::fileExists(doesNotExistPath));
EXPECT_FALSE(FileSystem::fileExists(symlinkPath)); // fileExists() follows symlinks.
EXPECT_EQ(FileSystem::fileType(symlinkPath), FileSystem::FileType::SymbolicLink);
EXPECT_TRUE(FileSystem::deleteFile(symlinkPath));
}
TEST_F(FileSystemTest, fileExistsSymlinkToSymlink)
{
// Create a valid symlink to a symlink to a regular file.
auto symlinkPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "symlink"_s);
EXPECT_TRUE(FileSystem::createSymbolicLink(tempFileSymlinkPath(), symlinkPath));
EXPECT_TRUE(FileSystem::fileExists(symlinkPath));
EXPECT_EQ(FileSystem::fileType(symlinkPath), FileSystem::FileType::SymbolicLink);
EXPECT_EQ(FileSystem::fileTypeFollowingSymlinks(symlinkPath), FileSystem::FileType::Regular);
// Break the symlink by deleting the target.
EXPECT_TRUE(FileSystem::deleteFile(tempFilePath()));
EXPECT_FALSE(FileSystem::fileExists(tempFilePath()));
EXPECT_FALSE(FileSystem::fileExists(tempFileSymlinkPath())); // fileExists() follows symlinks.
EXPECT_FALSE(FileSystem::fileExists(symlinkPath)); // fileExists() follows symlinks.
EXPECT_EQ(FileSystem::fileType(symlinkPath), FileSystem::FileType::SymbolicLink);
EXPECT_EQ(FileSystem::fileType(tempFileSymlinkPath()), FileSystem::FileType::SymbolicLink);
EXPECT_FALSE(FileSystem::fileTypeFollowingSymlinks(tempFileSymlinkPath()));
EXPECT_FALSE(FileSystem::fileTypeFollowingSymlinks(symlinkPath));
EXPECT_TRUE(FileSystem::deleteFile(symlinkPath));
}
TEST_F(FileSystemTest, deleteSymlink)
{
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
EXPECT_TRUE(FileSystem::fileExists(tempFileSymlinkPath()));
EXPECT_TRUE(FileSystem::deleteFile(tempFileSymlinkPath()));
// Should have deleted the symlink but not the target file.
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
EXPECT_FALSE(FileSystem::fileExists(tempFileSymlinkPath()));
}
TEST_F(FileSystemTest, deleteFile)
{
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
EXPECT_TRUE(FileSystem::deleteFile(tempFilePath()));
EXPECT_FALSE(FileSystem::fileExists(tempFilePath()));
EXPECT_FALSE(FileSystem::deleteFile(tempFilePath()));
}
TEST_F(FileSystemTest, deleteFileOnEmptyDirectory)
{
EXPECT_TRUE(FileSystem::fileExists(tempEmptyFolderPath()));
EXPECT_FALSE(FileSystem::deleteFile(tempEmptyFolderPath()));
EXPECT_TRUE(FileSystem::fileExists(tempEmptyFolderPath()));
}
TEST_F(FileSystemTest, deleteEmptyDirectory)
{
EXPECT_TRUE(FileSystem::fileExists(tempEmptyFolderPath()));
EXPECT_TRUE(FileSystem::deleteEmptyDirectory(tempEmptyFolderPath()));
EXPECT_FALSE(FileSystem::fileExists(tempEmptyFolderPath()));
EXPECT_FALSE(FileSystem::deleteEmptyDirectory(tempEmptyFolderPath()));
}
#if PLATFORM(MAC)
TEST_F(FileSystemTest, deleteEmptyDirectoryContainingDSStoreFile)
{
EXPECT_TRUE(FileSystem::fileExists(tempEmptyFolderPath()));
// Create .DSStore file.
auto dsStorePath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), ".DS_Store"_s);
auto dsStoreHandle = FileSystem::openFile(dsStorePath, FileSystem::FileOpenMode::Truncate);
dsStoreHandle.write(byteCast<uint8_t>(FileSystemTestData.span()));
dsStoreHandle = { };
EXPECT_TRUE(FileSystem::fileExists(dsStorePath));
EXPECT_TRUE(FileSystem::deleteEmptyDirectory(tempEmptyFolderPath()));
EXPECT_FALSE(FileSystem::fileExists(tempEmptyFolderPath()));
}
#endif
TEST_F(FileSystemTest, deleteEmptyDirectoryOnNonEmptyDirectory)
{
EXPECT_TRUE(FileSystem::fileExists(tempEmptyFolderPath()));
// Create .DSStore file.
auto dsStorePath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), ".DS_Store"_s);
auto dsStoreHandle = FileSystem::openFile(dsStorePath, FileSystem::FileOpenMode::Truncate);
dsStoreHandle.write(byteCast<uint8_t>(FileSystemTestData.span()));
dsStoreHandle = { };
EXPECT_TRUE(FileSystem::fileExists(dsStorePath));
// Create a dummy file.
auto dummyFilePath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "dummyFile"_s);
auto dummyFileHandle = FileSystem::openFile(dummyFilePath, FileSystem::FileOpenMode::Truncate);
dummyFileHandle.write(byteCast<uint8_t>(FileSystemTestData.span()));
dummyFileHandle = { };
EXPECT_TRUE(FileSystem::fileExists(dummyFilePath));
EXPECT_FALSE(FileSystem::deleteEmptyDirectory(tempEmptyFolderPath()));
EXPECT_TRUE(FileSystem::fileExists(tempEmptyFolderPath()));
EXPECT_TRUE(FileSystem::fileExists(dsStorePath));
EXPECT_TRUE(FileSystem::fileExists(dummyFilePath));
EXPECT_TRUE(FileSystem::deleteNonEmptyDirectory(tempEmptyFolderPath()));
EXPECT_FALSE(FileSystem::fileExists(tempEmptyFolderPath()));
}
TEST_F(FileSystemTest, deleteEmptyDirectoryOnARegularFile)
{
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
EXPECT_FALSE(FileSystem::deleteEmptyDirectory(tempFilePath()));
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
}
TEST_F(FileSystemTest, deleteEmptyDirectoryDoesNotExist)
{
auto doesNotExistPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
EXPECT_FALSE(FileSystem::fileExists(doesNotExistPath));
EXPECT_FALSE(FileSystem::deleteEmptyDirectory(doesNotExistPath));
}
TEST_F(FileSystemTest, moveFile)
{
auto destination = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "tempFile-moved"_s);
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
EXPECT_FALSE(FileSystem::fileExists(destination));
EXPECT_TRUE(FileSystem::moveFile(tempFilePath(), destination));
EXPECT_FALSE(FileSystem::fileExists(tempFilePath()));
EXPECT_TRUE(FileSystem::fileExists(destination));
EXPECT_FALSE(FileSystem::moveFile(tempFilePath(), destination));
}
TEST_F(FileSystemTest, moveFileOverwritesDestination)
{
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
EXPECT_TRUE(FileSystem::fileExists(tempEmptyFilePath()));
auto fileSize = FileSystem::fileSize(tempFilePath());
ASSERT_TRUE(fileSize);
EXPECT_GT(*fileSize, 0U);
fileSize = FileSystem::fileSize(tempEmptyFilePath());
ASSERT_TRUE(fileSize);
EXPECT_EQ(*fileSize, 0U);
EXPECT_TRUE(FileSystem::moveFile(tempFilePath(), tempEmptyFilePath()));
EXPECT_FALSE(FileSystem::fileExists(tempFilePath()));
EXPECT_TRUE(FileSystem::fileExists(tempEmptyFilePath()));
fileSize = FileSystem::fileSize(tempEmptyFilePath());
ASSERT_TRUE(fileSize);
EXPECT_GT(*fileSize, 0U);
}
TEST_F(FileSystemTest, moveDirectory)
{
auto temporaryTestFolder = FileSystem::createTemporaryFile("moveDirectoryTest"_s);
EXPECT_TRUE(FileSystem::deleteFile(temporaryTestFolder));
EXPECT_TRUE(FileSystem::makeAllDirectories(temporaryTestFolder));
auto testFilePath = FileSystem::pathByAppendingComponent(temporaryTestFolder, "testFile"_s);
auto fileHandle = FileSystem::openFile(testFilePath, FileSystem::FileOpenMode::Truncate);
fileHandle.write(byteCast<uint8_t>(FileSystemTestData.span()));
fileHandle = { };
EXPECT_TRUE(FileSystem::fileExists(testFilePath));
auto destinationPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "moveDirectoryTest"_s);
EXPECT_TRUE(FileSystem::moveFile(temporaryTestFolder, destinationPath));
EXPECT_FALSE(FileSystem::fileExists(temporaryTestFolder));
EXPECT_FALSE(FileSystem::fileExists(testFilePath));
EXPECT_TRUE(FileSystem::fileExists(destinationPath));
EXPECT_TRUE(FileSystem::fileExists(FileSystem::pathByAppendingComponent(destinationPath, "testFile"_s)));
EXPECT_FALSE(FileSystem::deleteEmptyDirectory(destinationPath));
EXPECT_TRUE(FileSystem::fileExists(destinationPath));
}
TEST_F(FileSystemTest, fileSize)
{
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
EXPECT_TRUE(FileSystem::fileExists(tempEmptyFilePath()));
auto fileSize = FileSystem::fileSize(tempFilePath());
ASSERT_TRUE(fileSize);
EXPECT_GT(*fileSize, 0U);
fileSize = FileSystem::fileSize(tempEmptyFilePath());
ASSERT_TRUE(fileSize);
EXPECT_EQ(*fileSize, 0U);
String fileThatDoesNotExist = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
fileSize = FileSystem::fileSize(fileThatDoesNotExist);
EXPECT_TRUE(!fileSize);
}
TEST_F(FileSystemTest, makeAllDirectories)
{
EXPECT_TRUE(FileSystem::fileExists(tempEmptyFolderPath()));
EXPECT_EQ(FileSystem::fileType(tempEmptyFolderPath()), FileSystem::FileType::Directory);
EXPECT_TRUE(FileSystem::makeAllDirectories(tempEmptyFolderPath()));
String subFolderPath = FileSystem::pathByAppendingComponents(tempEmptyFolderPath(), std::initializer_list<StringView>({ "subFolder1"_s, "subFolder2"_s, "subFolder3"_s }));
EXPECT_FALSE(FileSystem::fileExists(subFolderPath));
EXPECT_TRUE(FileSystem::makeAllDirectories(subFolderPath));
EXPECT_TRUE(FileSystem::fileExists(subFolderPath));
EXPECT_EQ(FileSystem::fileType(subFolderPath), FileSystem::FileType::Directory);
EXPECT_TRUE(FileSystem::deleteNonEmptyDirectory(tempEmptyFolderPath()));
EXPECT_FALSE(FileSystem::fileExists(subFolderPath));
String invalidFolderPath = makeString('\0', 'a');
EXPECT_FALSE(FileSystem::makeAllDirectories(invalidFolderPath));
EXPECT_FALSE(FileSystem::makeAllDirectories(emptyString()));
}
TEST_F(FileSystemTest, volumeFreeSpace)
{
auto freeSpace = FileSystem::volumeFreeSpace(tempFilePath());
ASSERT_TRUE(freeSpace);
EXPECT_GT(*freeSpace, 0U);
String fileThatDoesNotExist = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
EXPECT_FALSE(FileSystem::volumeFreeSpace(fileThatDoesNotExist));
}
TEST_F(FileSystemTest, createSymbolicLink)
{
auto symlinkPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "tempFile-symlink"_s);
EXPECT_FALSE(FileSystem::fileExists(symlinkPath));
EXPECT_TRUE(FileSystem::createSymbolicLink(tempFilePath(), symlinkPath));
EXPECT_TRUE(FileSystem::fileExists(symlinkPath));
EXPECT_EQ(FileSystem::fileType(symlinkPath), FileSystem::FileType::SymbolicLink);
EXPECT_TRUE(FileSystem::deleteFile(symlinkPath));
EXPECT_FALSE(FileSystem::fileExists(symlinkPath));
EXPECT_TRUE(FileSystem::fileExists(tempFilePath()));
}
#if OS(UNIX)
// FIXME: https://webkit.org/b/283603 Test crashes on Windows
TEST_F(FileSystemTest, createSymbolicLinkFolder)
{
auto symlinkPath = tempEmptyFolderSymlinkPath();
EXPECT_TRUE(FileSystem::deleteFile(symlinkPath));
EXPECT_FALSE(FileSystem::fileExists(symlinkPath));
EXPECT_TRUE(FileSystem::createSymbolicLink(tempEmptyFolderPath(), symlinkPath));
EXPECT_TRUE(FileSystem::fileExists(symlinkPath));
EXPECT_EQ(FileSystem::fileType(symlinkPath), FileSystem::FileType::SymbolicLink);
EXPECT_TRUE(FileSystem::deleteFile(symlinkPath));
EXPECT_FALSE(FileSystem::fileExists(symlinkPath));
EXPECT_TRUE(FileSystem::fileExists(tempEmptyFolderPath()));
}
#endif
TEST_F(FileSystemTest, createSymbolicLinkFileDoesNotExist)
{
String fileThatDoesNotExist = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
EXPECT_FALSE(FileSystem::fileExists(fileThatDoesNotExist));
auto symlinkPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist-symlink"_s);
EXPECT_FALSE(FileSystem::fileExists(symlinkPath));
EXPECT_TRUE(FileSystem::createSymbolicLink(fileThatDoesNotExist, symlinkPath));
EXPECT_FALSE(FileSystem::fileExists(symlinkPath));
}
TEST_F(FileSystemTest, createHardLink)
{
auto hardlinkPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "tempFile-hardlink"_s);
EXPECT_FALSE(FileSystem::fileExists(hardlinkPath));
auto fileSize = FileSystem::fileSize(tempFilePath());
ASSERT_TRUE(fileSize);
EXPECT_GT(*fileSize, 0U);
EXPECT_TRUE(FileSystem::hardLink(tempFilePath(), hardlinkPath));
EXPECT_TRUE(FileSystem::fileExists(hardlinkPath));
auto linkFileSize = FileSystem::fileSize(hardlinkPath);
ASSERT_TRUE(linkFileSize);
EXPECT_EQ(*linkFileSize, *fileSize);
EXPECT_EQ(FileSystem::fileType(hardlinkPath), FileSystem::FileType::Regular);
EXPECT_TRUE(FileSystem::deleteFile(tempFilePath()));
EXPECT_FALSE(FileSystem::fileExists(tempFilePath()));
EXPECT_TRUE(FileSystem::fileExists(hardlinkPath));
linkFileSize = FileSystem::fileSize(hardlinkPath);
ASSERT_TRUE(linkFileSize);
EXPECT_EQ(*linkFileSize, *fileSize);
}
TEST_F(FileSystemTest, createHardLinkOrCopyFile)
{
auto hardlinkPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "tempFile-hardlink"_s);
EXPECT_FALSE(FileSystem::fileExists(hardlinkPath));
auto fileSize = FileSystem::fileSize(tempFilePath());
ASSERT_TRUE(fileSize);
EXPECT_GT(*fileSize, 0U);
EXPECT_TRUE(FileSystem::hardLinkOrCopyFile(tempFilePath(), hardlinkPath));
EXPECT_TRUE(FileSystem::fileExists(hardlinkPath));
auto linkFileSize = FileSystem::fileSize(hardlinkPath);
ASSERT_TRUE(linkFileSize);
EXPECT_EQ(*linkFileSize, *fileSize);
EXPECT_EQ(FileSystem::fileType(hardlinkPath), FileSystem::FileType::Regular);
EXPECT_TRUE(FileSystem::deleteFile(tempFilePath()));
EXPECT_FALSE(FileSystem::fileExists(tempFilePath()));
EXPECT_TRUE(FileSystem::fileExists(hardlinkPath));
linkFileSize = FileSystem::fileSize(hardlinkPath);
ASSERT_TRUE(linkFileSize);
EXPECT_EQ(*linkFileSize, *fileSize);
}
TEST_F(FileSystemTest, hardLinkCount)
{
auto linkCount = FileSystem::hardLinkCount(tempFilePath());
ASSERT_TRUE(!!linkCount);
EXPECT_EQ(*linkCount, 1U);
auto hardlink1Path = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "tempFile-hardlink1"_s);
EXPECT_TRUE(FileSystem::hardLink(tempFilePath(), hardlink1Path));
linkCount = FileSystem::hardLinkCount(tempFilePath());
ASSERT_TRUE(!!linkCount);
EXPECT_EQ(*linkCount, 2U);
auto hardlink2Path = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "tempFile-hardlink2"_s);
EXPECT_TRUE(FileSystem::hardLink(tempFilePath(), hardlink2Path));
linkCount = FileSystem::hardLinkCount(tempFilePath());
ASSERT_TRUE(!!linkCount);
EXPECT_EQ(*linkCount, 3U);
EXPECT_TRUE(FileSystem::deleteFile(hardlink1Path));
linkCount = FileSystem::hardLinkCount(tempFilePath());
ASSERT_TRUE(!!linkCount);
EXPECT_EQ(*linkCount, 2U);
EXPECT_TRUE(FileSystem::deleteFile(hardlink2Path));
linkCount = FileSystem::hardLinkCount(tempFilePath());
ASSERT_TRUE(!!linkCount);
EXPECT_EQ(*linkCount, 1U);
EXPECT_TRUE(FileSystem::deleteFile(tempFilePath()));
linkCount = FileSystem::hardLinkCount(tempFilePath());
EXPECT_TRUE(!linkCount);
}
static void runGetFileModificationTimeTest(const String& path, Function<std::optional<WallTime>(const String&)>&& fileModificationTime)
{
auto modificationTime = fileModificationTime(path);
EXPECT_TRUE(!!modificationTime);
if (!modificationTime)
return;
unsigned timeout = 0;
while (*modificationTime >= WallTime::now() && ++timeout < 20)
TestWebKitAPI::Util::runFor(0.1_s);
EXPECT_LT(modificationTime->secondsSinceEpoch().value(), WallTime::now().secondsSinceEpoch().value());
auto timeBeforeModification = WallTime::now();
TestWebKitAPI::Util::runFor(2_s);
// Modify the file.
auto fileHandle = FileSystem::openFile(path, FileSystem::FileOpenMode::ReadWrite);
EXPECT_TRUE(!!fileHandle);
fileHandle.write(byteCast<uint8_t>("foo"_span));
fileHandle = { };
auto newModificationTime = fileModificationTime(path);
EXPECT_TRUE(!!newModificationTime);
if (!newModificationTime)
return;
EXPECT_GT(newModificationTime->secondsSinceEpoch().value(), modificationTime->secondsSinceEpoch().value());
EXPECT_GT(newModificationTime->secondsSinceEpoch().value(), timeBeforeModification.secondsSinceEpoch().value());
}
TEST_F(FileSystemTest, fileModificationTime)
{
runGetFileModificationTimeTest(tempFilePath(), [](const String& path) {
return FileSystem::fileModificationTime(path);
});
}
TEST_F(FileSystemTest, updateFileModificationTime)
{
auto modificationTime = FileSystem::fileModificationTime(tempFilePath());
ASSERT_TRUE(!!modificationTime);
unsigned timeout = 0;
while (*modificationTime >= WallTime::now() && ++timeout < 20)
TestWebKitAPI::Util::runFor(0.1_s);
EXPECT_LT(modificationTime->secondsSinceEpoch().value(), WallTime::now().secondsSinceEpoch().value());
TestWebKitAPI::Util::runFor(1_s);
EXPECT_TRUE(FileSystem::updateFileModificationTime(tempFilePath()));
auto newModificationTime = FileSystem::fileModificationTime(tempFilePath());
ASSERT_TRUE(!!newModificationTime);
EXPECT_GT(newModificationTime->secondsSinceEpoch().value(), modificationTime->secondsSinceEpoch().value());
auto doesNotExistPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
EXPECT_FALSE(FileSystem::updateFileModificationTime(doesNotExistPath));
}
TEST_F(FileSystemTest, pathFileName)
{
auto testPath = FileSystem::pathByAppendingComponents(tempEmptyFolderPath(), std::initializer_list<StringView>({ "subfolder"_s, "filename.txt"_s }));
EXPECT_STREQ("filename.txt", FileSystem::pathFileName(testPath).utf8().data());
#if OS(UNIX)
EXPECT_STREQ(".", FileSystem::pathFileName("."_s).utf8().data());
EXPECT_STREQ("..", FileSystem::pathFileName(".."_s).utf8().data());
EXPECT_STREQ("", FileSystem::pathFileName("/"_s).utf8().data());
EXPECT_STREQ(".", FileSystem::pathFileName("/foo/."_s).utf8().data());
EXPECT_STREQ("..", FileSystem::pathFileName("/foo/.."_s).utf8().data());
EXPECT_STREQ("", FileSystem::pathFileName("/foo/"_s).utf8().data());
EXPECT_STREQ("host", FileSystem::pathFileName("//host"_s).utf8().data());
#endif
#if OS(WINDOWS)
EXPECT_STREQ("", FileSystem::pathFileName("C:\\"_s).utf8().data());
EXPECT_STREQ("foo", FileSystem::pathFileName("C:\\foo"_s).utf8().data());
EXPECT_STREQ("", FileSystem::pathFileName("C:\\foo\\"_s).utf8().data());
EXPECT_STREQ("bar.txt", FileSystem::pathFileName("C:\\foo\\bar.txt"_s).utf8().data());
#endif
}
TEST_F(FileSystemTest, parentPath)
{
auto testPath = FileSystem::pathByAppendingComponents(tempEmptyFolderPath(), std::initializer_list<StringView>({ "subfolder"_s, "filename.txt"_s }));
EXPECT_STREQ(FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "subfolder"_s).utf8().data(), FileSystem::parentPath(testPath).utf8().data());
#if OS(UNIX)
EXPECT_STREQ("/var/tmp", FileSystem::parentPath("/var/tmp/example.txt"_s).utf8().data());
EXPECT_STREQ("/var/tmp", FileSystem::parentPath("/var/tmp/"_s).utf8().data());
EXPECT_STREQ("/var/tmp", FileSystem::parentPath("/var/tmp/."_s).utf8().data());
EXPECT_STREQ("/", FileSystem::parentPath("/"_s).utf8().data());
#endif
#if OS(WINDOWS)
EXPECT_STREQ("C:\\foo", FileSystem::parentPath("C:\\foo\\example.txt"_s).utf8().data());
EXPECT_STREQ("C:\\", FileSystem::parentPath("C:\\"_s).utf8().data());
#endif
}
TEST_F(FileSystemTest, pathByAppendingComponent)
{
#if OS(UNIX)
EXPECT_STREQ("/var", FileSystem::pathByAppendingComponent("/"_s, "var"_s).utf8().data());
EXPECT_STREQ("/var/tmp", FileSystem::pathByAppendingComponent("/var/"_s, "tmp"_s).utf8().data());
EXPECT_STREQ("/var/tmp", FileSystem::pathByAppendingComponent("/var"_s, "tmp"_s).utf8().data());
EXPECT_STREQ("/var/tmp/file.txt", FileSystem::pathByAppendingComponent("/var/tmp"_s, "file.txt"_s).utf8().data());
EXPECT_STREQ("/var/", FileSystem::pathByAppendingComponent("/var"_s, ""_s).utf8().data());
EXPECT_STREQ("/var/", FileSystem::pathByAppendingComponent("/var/"_s, ""_s).utf8().data());
#endif
#if OS(WINDOWS)
EXPECT_STREQ("C:\\Foo", FileSystem::pathByAppendingComponent("C:\\"_s, "Foo"_s).utf8().data());
EXPECT_STREQ("C:\\Foo\\Bar", FileSystem::pathByAppendingComponent("C:\\Foo"_s, "Bar"_s).utf8().data());
EXPECT_STREQ("C:\\Foo\\Bar\\File.txt", FileSystem::pathByAppendingComponent("C:\\Foo\\Bar"_s, "File.txt"_s).utf8().data());
#endif
}
TEST_F(FileSystemTest, pathByAppendingComponents)
{
EXPECT_STREQ(tempEmptyFolderPath().utf8().data(), FileSystem::pathByAppendingComponents(tempEmptyFolderPath(), { }).utf8().data());
EXPECT_STREQ(FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "file.txt"_s).utf8().data(), FileSystem::pathByAppendingComponents(tempEmptyFolderPath(), std::initializer_list<StringView>({ "file.txt"_s })).utf8().data());
#if OS(UNIX)
EXPECT_STREQ("/var/tmp/file.txt", FileSystem::pathByAppendingComponents("/"_s, std::initializer_list<StringView>({ "var"_s, "tmp"_s, "file.txt"_s })).utf8().data());
EXPECT_STREQ("/var/tmp/file.txt", FileSystem::pathByAppendingComponents("/var"_s, std::initializer_list<StringView>({ "tmp"_s, "file.txt"_s })).utf8().data());
EXPECT_STREQ("/var/tmp/file.txt", FileSystem::pathByAppendingComponents("/var/"_s, std::initializer_list<StringView>({ "tmp"_s, "file.txt"_s })).utf8().data());
EXPECT_STREQ("/var/tmp/file.txt", FileSystem::pathByAppendingComponents("/var/tmp"_s, std::initializer_list<StringView>({ "file.txt"_s })).utf8().data());
#endif
#if OS(WINDOWS)
EXPECT_STREQ("C:\\Foo\\Bar\\File.txt", FileSystem::pathByAppendingComponents("C:\\"_s, std::initializer_list<StringView>({ "Foo"_s, "Bar"_s, "File.txt"_s })).utf8().data());
EXPECT_STREQ("C:\\Foo\\Bar\\File.txt", FileSystem::pathByAppendingComponents("C:\\Foo"_s, std::initializer_list<StringView>({ "Bar"_s, "File.txt"_s })).utf8().data());
EXPECT_STREQ("C:\\Foo\\Bar\\File.txt", FileSystem::pathByAppendingComponents("C:\\Foo\\"_s, std::initializer_list<StringView>({ "Bar"_s, "File.txt"_s })).utf8().data());
EXPECT_STREQ("C:\\Foo\\Bar\\File.txt", FileSystem::pathByAppendingComponents("C:\\Foo\\Bar"_s, std::initializer_list<StringView>({ "File.txt"_s })).utf8().data());
EXPECT_STREQ("C:\\Foo\\Bar\\File.txt", FileSystem::pathByAppendingComponents("C:\\Foo\\Bar\\"_s, std::initializer_list<StringView>({ "File.txt"_s })).utf8().data());
#endif
}
TEST_F(FileSystemTest, listDirectory)
{
createTestFile(FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "a.txt"_s));
createTestFile(FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "b.txt"_s));
createTestFile(FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "bar.png"_s));
createTestFile(FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "foo.png"_s));
FileSystem::makeAllDirectories(FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "subfolder"_s));
createTestFile(FileSystem::pathByAppendingComponents(tempEmptyFolderPath(), std::initializer_list<StringView>({ "subfolder"_s, "c.txt"_s })));
createTestFile(FileSystem::pathByAppendingComponents(tempEmptyFolderPath(), std::initializer_list<StringView>({ "subfolder"_s, "d.txt"_s })));
auto matches = FileSystem::listDirectory(tempEmptyFolderPath());
ASSERT_EQ(matches.size(), 5U);
std::ranges::sort(matches, WTF::codePointCompareLessThan);
EXPECT_STREQ(matches[0].utf8().data(), "a.txt");
EXPECT_STREQ(matches[1].utf8().data(), "b.txt");
EXPECT_STREQ(matches[2].utf8().data(), "bar.png");
EXPECT_STREQ(matches[3].utf8().data(), "foo.png");
EXPECT_STREQ(matches[4].utf8().data(), "subfolder");
matches = FileSystem::listDirectory(FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "subfolder"_s));
ASSERT_EQ(matches.size(), 2U);
std::ranges::sort(matches, WTF::codePointCompareLessThan);
EXPECT_STREQ(matches[0].utf8().data(), "c.txt");
EXPECT_STREQ(matches[1].utf8().data(), "d.txt");
matches = FileSystem::listDirectory(FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s));
ASSERT_EQ(matches.size(), 0U);
matches = FileSystem::listDirectory(FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "a.txt"_s));
ASSERT_EQ(matches.size(), 0U);
EXPECT_TRUE(FileSystem::deleteNonEmptyDirectory(tempEmptyFolderPath()));
}
#if OS(UNIX)
// FIXME: https://webkit.org/b/283603 Test crashes on Windows
TEST_F(FileSystemTest, realPath)
{
auto doesNotExistPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s);
EXPECT_STREQ(FileSystem::realPath(doesNotExistPath).utf8().data(), doesNotExistPath.utf8().data());
auto resolvedTempFilePath = FileSystem::realPath(tempFilePath());
EXPECT_STREQ(FileSystem::realPath(resolvedTempFilePath).utf8().data(), resolvedTempFilePath.utf8().data());
EXPECT_STREQ(FileSystem::realPath(tempFileSymlinkPath()).utf8().data(), resolvedTempFilePath.utf8().data()); // Should resolve file symlink.
auto resolvedTempEmptyFolderPath = FileSystem::realPath(tempEmptyFolderPath());
EXPECT_STREQ(FileSystem::realPath(resolvedTempEmptyFolderPath).utf8().data(), resolvedTempEmptyFolderPath.utf8().data());
EXPECT_STREQ(FileSystem::realPath(tempEmptyFolderSymlinkPath()).utf8().data(), resolvedTempEmptyFolderPath.utf8().data()); // Should resolve directory symlink.
// Symlink to symlink case.
auto symlinkToSymlinkPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "symlinkToSymlink"_s);
EXPECT_TRUE(FileSystem::createSymbolicLink(tempFileSymlinkPath(), symlinkToSymlinkPath));
EXPECT_STREQ(FileSystem::realPath(symlinkToSymlinkPath).utf8().data(), resolvedTempFilePath.utf8().data()); // Should resolve all symlinks.
auto subFolderPath = FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "subfolder"_s);
FileSystem::makeAllDirectories(subFolderPath);
auto resolvedSubFolderPath = FileSystem::realPath(subFolderPath);
EXPECT_STREQ(FileSystem::realPath(FileSystem::pathByAppendingComponent(subFolderPath, ".."_s)).utf8().data(), resolvedTempEmptyFolderPath.utf8().data()); // Should resolve "..".
EXPECT_STREQ(FileSystem::realPath(FileSystem::pathByAppendingComponents(subFolderPath, std::initializer_list<StringView>({ ".."_s, "subfolder"_s }))).utf8().data(), resolvedSubFolderPath.utf8().data()); // Should resolve "..".
EXPECT_STREQ(FileSystem::realPath(FileSystem::pathByAppendingComponents(subFolderPath, std::initializer_list<StringView>({ ".."_s, "."_s, "."_s, "subfolder"_s }))).utf8().data(), resolvedSubFolderPath.utf8().data()); // Should resolve ".." and "."
}
#endif
TEST_F(FileSystemTest, readEntireFile)
{
FileSystem::FileHandle fileHandle;
EXPECT_FALSE(fileHandle.readAll());
EXPECT_FALSE(FileSystem::readEntireFile(emptyString()));
EXPECT_FALSE(FileSystem::readEntireFile(FileSystem::pathByAppendingComponent(tempEmptyFolderPath(), "does-not-exist"_s)));
EXPECT_FALSE(FileSystem::readEntireFile(tempEmptyFilePath()));
auto buffer = FileSystem::readEntireFile(tempFilePath());
EXPECT_TRUE(buffer);
auto contents = String { byteCast<Latin1Character>(buffer.value().span()) }.utf8();
EXPECT_STREQ(contents.data(), FileSystemTestData);
}
TEST_F(FileSystemTest, makeSafeToUseMemoryMapForPath)
{
EXPECT_TRUE(FileSystem::makeSafeToUseMemoryMapForPath(tempFilePath()));
auto result = FileSystem::makeSafeToUseMemoryMapForPath("Thisisnotarealfile"_str);
#if PLATFORM(IOS_FAMILY) && !PLATFORM(IOS_FAMILY_SIMULATOR)
// NSFileProtectionKey only actually means anything on-device.
EXPECT_FALSE(result);
#else
EXPECT_TRUE(result);
#endif
}
TEST_F(FileSystemTest, isAncestor)
{
Vector<std::pair<std::pair<const char*, const char*>, bool>> narrowString {
{ { "/a/b/c/", "/a/b/c/d" }, true },
{ { "/a/b/c", "/a/b/c/d/e/.." }, true },
{ { "/a/b/c/.", "/a/b/c/d" }, true },
{ { "/a/b/c", "/a/b/c" }, false },
{ { "/a/b/c/x/..", "/a/b/c" }, false },
{ { "/a/b/c/dir1", "/a/b/c/dir2" }, false },
{ { "/a/b/c", "/a/b/c/" }, false },
{ { "/a/b/c", "/a/b/c/." }, false },
{ { "a/b/c", "/a/b/c/" }, false },
{ { "a/b/c", "a/b/c/" }, false },
{ { "/a/b/c", "a/b/c/" }, false }
};
std::ranges::for_each(narrowString, [](auto input) {
EXPECT_EQ(
input.second,
FileSystem::isAncestor(
ASCIILiteral::fromLiteralUnsafe(input.first.first),
ASCIILiteral::fromLiteralUnsafe(input.first.second)
)
);
}
);
Vector<std::pair<std::pair<const char16_t *, const char16_t *>, bool>> wideString {
{ { u"/a/b/c/", u"/a/b/c/d" }, true },
{ { u"/a/b/c", u"/a/b/c/d/e/.." }, true },
{ { u"/a/b/c/.", u"/a/b/c/d" }, true },
{ { u"/a/b/c", u"/a/b/c" }, false },
{ { u"/a/b/c/x/..", u"/a/b/c" }, false },
{ { u"/a/b/c/dir1", u"/a/b/c/dir2" }, false },
{ { u"/a/b/c", u"/a/b/c/" }, false },
{ { u"/a/b/c", u"/a/b/c/." }, false },
{ { u"a/b/c", u"/a/b/c/" }, false },
{ { u"a/b/c", u"a/b/c/" }, false },
{ { u"/a/b/c", u"a/b/c/" }, false }
};
std::ranges::for_each(wideString, [](auto input) {
EXPECT_EQ(
input.second,
FileSystem::isAncestor(
std::span<const char16_t> { static_cast<const char16_t *>(input.first.first), std::char_traits<char16_t>::length(input.first.first) },
std::span<const char16_t> { static_cast<const char16_t*>(input.first.second), std::char_traits<char16_t>::length(input.first.second) }
)
);
}
);
}
} // namespace TestWebKitAPI