Files
2026-07-13 12:47:42 +08:00

405 lines
13 KiB
C++

// Copyright 2025-present the zvec project
//
// 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 <cstring>
#include <vector>
#include <ailego/utility/memory_helper.h>
#include <gtest/gtest.h>
#include <zvec/ailego/io/file.h>
using namespace zvec::ailego;
TEST(File, General) {
EXPECT_TRUE(File::IsDirectory("."));
EXPECT_TRUE(File::IsDirectory(".."));
EXPECT_TRUE(File::IsDirectory("../"));
EXPECT_TRUE(File::IsDirectory("..//"));
EXPECT_TRUE(File::IsDirectory("..//"));
EXPECT_FALSE(File::IsSymbolicLink("."));
EXPECT_FALSE(File::IsSymbolicLink(".."));
EXPECT_FALSE(File::IsSymbolicLink("../"));
EXPECT_FALSE(File::IsSymbolicLink("..//"));
EXPECT_FALSE(File::IsSymbolicLink("..//"));
EXPECT_FALSE(File::IsRegular("."));
EXPECT_FALSE(File::IsRegular(".."));
EXPECT_FALSE(File::IsRegular("../"));
EXPECT_FALSE(File::IsRegular("..//"));
EXPECT_FALSE(File::IsRegular("..//"));
EXPECT_TRUE(File::IsExist("."));
EXPECT_TRUE(File::IsExist(".."));
EXPECT_TRUE(File::IsExist("../"));
EXPECT_TRUE(File::IsExist("..//"));
EXPECT_TRUE(File::IsExist("..//"));
}
TEST(File, MakePath) {
EXPECT_FALSE(File::MakePath(""));
std::cout << FileHelper::GetLastErrorString() << std::endl;
EXPECT_TRUE(File::MakePath("."));
EXPECT_TRUE(File::MakePath(".."));
EXPECT_TRUE(File::MakePath("../"));
EXPECT_TRUE(File::MakePath("..//"));
EXPECT_TRUE(File::MakePath("..//"));
EXPECT_TRUE(File::MakePath("/"));
EXPECT_TRUE(File::MakePath("./file_test_makepath"));
EXPECT_TRUE(File::MakePath("file_test_makepath"));
EXPECT_TRUE(File::MakePath("file_test_makepath/1/2/3/"));
EXPECT_TRUE(File::MakePath("file_test_makepath/1/2/3"));
}
bool TouchFile(const char *path) {
std::string buf(path);
char *sp = (char *)strrchr(buf.data(), '/');
*sp = '\0';
File::MakePath(buf.data());
*sp = '/';
File file;
return file.create(path, 0);
}
TEST(File, RemoveDirectory) {
EXPECT_TRUE(File::MakePath("file_test_rmdir/1/2/3"));
EXPECT_TRUE(File::MakePath("file_test_rmdir/a/b/c/d"));
EXPECT_TRUE(File::MakePath("file_test_rmdir/1/a/b/c/d"));
EXPECT_TRUE(File::MakePath("file_test_rmdir/1/2/a/b/c/d"));
EXPECT_TRUE(File::MakePath("file_test_rmdir/1/2/3/a/b/c/d"));
EXPECT_TRUE(File::MakePath("file_test_rmdir/a/1/2/3"));
EXPECT_TRUE(File::MakePath("file_test_rmdir/a/b/1/2/3"));
EXPECT_TRUE(File::MakePath("file_test_rmdir/a/b/c/1/2/3"));
EXPECT_TRUE(TouchFile("file_test_rmdir/a/b/c/1/2/3/A"));
EXPECT_TRUE(TouchFile("file_test_rmdir/a/b/c/1/2/3/B"));
EXPECT_TRUE(TouchFile("file_test_rmdir/C"));
EXPECT_TRUE(TouchFile("file_test_rmdir/D"));
EXPECT_TRUE(TouchFile("file_test_rmdir/1/2/3/E"));
EXPECT_TRUE(TouchFile("file_test_rmdir/a/b/c/d/F"));
EXPECT_TRUE(TouchFile("file_test_rmdir/1/a/b/c/d/G"));
EXPECT_TRUE(TouchFile("file_test_rmdir/1/2/a/b/c/d/H"));
EXPECT_TRUE(TouchFile("file_test_rmdir/1/2/3/a/b/c/d/I"));
EXPECT_TRUE(TouchFile("file_test_rmdir/a/1/2/3/J"));
EXPECT_TRUE(TouchFile("file_test_rmdir/a/b/1/2/3/K"));
EXPECT_TRUE(TouchFile("file_test_rmdir/1/2/3/M"));
EXPECT_TRUE(TouchFile("file_test_rmdir/1/2/a/b/c/d/N"));
EXPECT_FALSE(File::RemoveDirectory("file_test_rmdir/1/2/a/b/c/d/N"));
EXPECT_FALSE(File::RemoveDirectory("file_test_rmdir/1/2/3/a/b/c/d/I"));
EXPECT_FALSE(File::RemoveDirectory("file_test_rmdir/C"));
EXPECT_FALSE(File::RemoveDirectory("file_test_rmdir/D"));
EXPECT_TRUE(File::IsDirectory("file_test_rmdir/"));
EXPECT_TRUE(File::IsDirectory("file_test_makepath/"));
EXPECT_TRUE(File::RemoveDirectory("file_test_rmdir/"));
EXPECT_TRUE(File::RemoveDirectory("file_test_makepath"));
}
TEST(File, RemovePath) {
EXPECT_TRUE(File::MakePath("file_test_rmpath/1/2/3"));
EXPECT_TRUE(File::MakePath("file_test_rmpath/a/b/c/d"));
EXPECT_TRUE(File::MakePath("file_test_rmpath/1/a/b/c/d"));
EXPECT_TRUE(File::MakePath("file_test_rmpath/1/2/a/b/c/d"));
EXPECT_TRUE(File::MakePath("file_test_rmpath/1/2/3/a/b/c/d"));
EXPECT_TRUE(File::MakePath("file_test_rmpath/a/1/2/3"));
EXPECT_TRUE(File::MakePath("file_test_rmpath/a/b/1/2/3"));
EXPECT_TRUE(File::MakePath("file_test_rmpath/a/b/c/1/2/3"));
EXPECT_TRUE(TouchFile("file_test_rmpath/a/b/c/1/2/3/A"));
EXPECT_TRUE(TouchFile("file_test_rmpath/a/b/c/1/2/3/B"));
EXPECT_TRUE(TouchFile("file_test_rmpath/C"));
EXPECT_TRUE(TouchFile("file_test_rmpath/D"));
EXPECT_TRUE(TouchFile("file_test_rmpath/1/2/3/E"));
EXPECT_TRUE(TouchFile("file_test_rmpath/a/b/c/d/F"));
EXPECT_TRUE(TouchFile("file_test_rmpath/1/a/b/c/d/G"));
EXPECT_TRUE(TouchFile("file_test_rmpath/1/2/a/b/c/d/H"));
EXPECT_TRUE(TouchFile("file_test_rmpath/1/2/3/a/b/c/d/I"));
EXPECT_TRUE(TouchFile("file_test_rmpath/a/1/2/3/J"));
EXPECT_TRUE(TouchFile("file_test_rmpath/a/b/1/2/3/K"));
EXPECT_TRUE(TouchFile("file_test_rmpath/1/2/3/M"));
EXPECT_TRUE(TouchFile("file_test_rmpath/1/2/a/b/c/d/N"));
EXPECT_TRUE(File::IsExist("file_test_rmpath/1/2/a/b/c/d/N"));
EXPECT_TRUE(File::IsDirectory("file_test_rmpath/"));
EXPECT_TRUE(File::RemovePath("file_test_rmpath/"));
EXPECT_TRUE(File::MakePath("file_test_rmpath/AAA"));
EXPECT_TRUE(File::MakePath("file_test_rmpath/BBB"));
EXPECT_TRUE(TouchFile("file_test_rmpath/CCC"));
EXPECT_TRUE(TouchFile("file_test_rmpath/DDD"));
EXPECT_TRUE(File::IsExist("file_test_rmpath/BBB"));
EXPECT_FALSE(File::RemovePath("file_test_rmpath/CCC/"));
EXPECT_FALSE(File::RemovePath("file_test_rmpath/DDD/"));
EXPECT_TRUE(File::RemovePath("file_test_rmpath/CCC"));
EXPECT_TRUE(File::RemovePath("file_test_rmpath/DDD"));
EXPECT_TRUE(File::RemovePath("file_test_rmpath"));
}
TEST(File, CreateAndOpen) {
const char *file_path = "file_create_testing.tmp";
size_t file_size = 12 * 1022 * 1021;
File::Delete(file_path);
EXPECT_FALSE(File::IsRegular(file_path));
{
File file;
EXPECT_FALSE(file.is_valid());
EXPECT_TRUE(file.create(file_path, file_size, true));
EXPECT_TRUE(file.is_valid());
EXPECT_TRUE(File::IsRegular(file_path));
EXPECT_EQ(file_size, file.size());
}
// create again with exist file
{
File file;
EXPECT_FALSE(file.is_valid());
EXPECT_TRUE(file.create(file_path, file_size / 10));
EXPECT_TRUE(file.is_valid());
EXPECT_FALSE(file.read_only());
EXPECT_EQ(file_size / 10, file.size());
}
{
File file;
EXPECT_FALSE(file.is_valid());
EXPECT_TRUE(file.create(file_path, file_size * 3, true));
EXPECT_TRUE(file.is_valid());
EXPECT_FALSE(file.read_only());
EXPECT_EQ(file_size * 3, file.size());
}
{
File file;
EXPECT_TRUE(file.open(file_path, true, true));
EXPECT_TRUE(file.is_valid());
EXPECT_TRUE(file.read_only());
EXPECT_EQ(file_size * 3, file.size());
}
{
File file;
EXPECT_TRUE(file.open(file_path, false, true));
EXPECT_TRUE(file.is_valid());
EXPECT_FALSE(file.read_only());
EXPECT_EQ(file_size * 3, file.size());
}
File::Delete(file_path);
}
TEST(File, ReadAndWrite) {
const char *file_path = "file_read_testing.tmp";
size_t file_size = 2u * 1024u * 1024u + 12u * 1024;
File::Delete(file_path);
EXPECT_FALSE(File::IsRegular(file_path));
File file;
EXPECT_FALSE(file.is_valid());
EXPECT_TRUE(file.create(file_path, file_size));
EXPECT_TRUE(File::IsRegular(file_path));
EXPECT_TRUE(file.is_valid());
EXPECT_EQ(0, file.offset());
EXPECT_EQ(file_size, file.size());
std::string buf;
buf.resize(file_size, 0x55);
ASSERT_EQ(file_size, buf.size());
EXPECT_EQ(file_size, file.write(buf.data(), buf.size()));
EXPECT_EQ(file_size, file.size());
EXPECT_EQ((ssize_t)buf.size(), file.offset());
EXPECT_TRUE(file.flush());
buf.clear();
buf.resize(file_size);
file.reset();
EXPECT_EQ(file_size, file.read((void *)buf.data(), buf.size()));
File::Delete(file_path);
}
TEST(File, MemoryMap) {
const char *file_path = "file_map_testing.tmp";
size_t file_size = 2u * 1024u * 1024u + 12u * 1024;
size_t map_offset = MemoryHelper::PageSize() * 16;
size_t map_size = file_size - map_offset;
File::Delete(file_path);
EXPECT_FALSE(File::IsRegular(file_path));
File file;
EXPECT_FALSE(file.is_valid());
EXPECT_TRUE(file.create(file_path, file_size));
EXPECT_TRUE(File::IsRegular(file_path));
EXPECT_EQ(file_size, file.size());
void *addr = file.map(map_offset, map_size, 0);
EXPECT_TRUE(addr != nullptr);
EXPECT_TRUE(File::MemoryFlush(addr, map_size));
File::MemoryUnmap(addr, map_size);
file.close();
EXPECT_TRUE(file.open(file_path, true));
EXPECT_EQ(file_size, file.size());
addr = file.map(map_offset, map_size, 0);
EXPECT_TRUE(addr != nullptr);
EXPECT_TRUE(File::MemoryFlush(addr, map_size));
File::MemoryUnmap(addr, map_size);
// void *addr1 = file.map(map_offset, map_size, 0);
// void *addr2 = file.map(map_offset, map_size, 0);
// EXPECT_EQ(addr1, addr2);
file.close();
EXPECT_TRUE(file.open(file_path, true));
EXPECT_EQ(file_size, file.size());
addr = file.map(map_offset, map_size, File::MMAP_SHARED);
EXPECT_TRUE(addr != nullptr);
EXPECT_TRUE(File::MemoryFlush(addr, map_size));
#if defined(__linux) || defined(__linux__) || defined(__NetBSD__)
EXPECT_TRUE(File::MemoryRemap(addr, map_size, addr, map_size * 2));
addr = File::MemoryRemap(addr, map_size, nullptr, map_size * 3);
EXPECT_TRUE(addr);
#endif
File::MemoryUnmap(addr, map_size);
file.close();
#if !defined(_WIN32)
addr = File::MemoryMap(map_size, 0);
EXPECT_TRUE(addr != nullptr);
File::MemoryUnmap(addr, map_size);
addr = File::MemoryMap(map_size, File::MMAP_SHARED);
EXPECT_TRUE(addr != nullptr);
File::MemoryUnmap(addr, map_size);
#endif
}
TEST(File, Append) {
const char *file_path = "file_append_testing.tmp";
File file;
EXPECT_FALSE(file.is_valid());
EXPECT_TRUE(file.create(file_path, MemoryHelper::PageSize()));
EXPECT_TRUE(File::IsRegular(file_path));
std::string padding;
padding.resize(MemoryHelper::PageSize());
for (size_t i = 0; i < 10; ++i) {
EXPECT_EQ(padding.size(),
file.write(file.size(), padding.data(), padding.size()));
}
EXPECT_EQ(padding.size() * 11, file.size());
file.truncate(padding.size() * 7);
EXPECT_EQ(padding.size() * 7, file.size());
file.truncate(padding.size() * 16);
EXPECT_EQ(padding.size() * 16, file.size());
file.close();
}
TEST(File, Seek) {
const char *file_path = "file_seek_testing.tmp";
File file;
EXPECT_FALSE(file.is_valid());
EXPECT_TRUE(file.create(file_path, 0));
EXPECT_TRUE(File::IsRegular(file_path));
std::string padding;
padding.resize(MemoryHelper::PageSize());
for (size_t i = 0; i < 10; ++i) {
EXPECT_EQ(padding.size(), file.write(padding.data(), padding.size()));
}
EXPECT_EQ(padding.size() * 10, (size_t)file.size());
EXPECT_EQ(padding.size() * 10, (size_t)file.offset());
EXPECT_TRUE(file.seek(0, File::Origin::Begin));
EXPECT_EQ(0, file.offset());
EXPECT_TRUE(file.seek(-20, File::Origin::End));
EXPECT_EQ((ssize_t)file.size() - 20, file.offset());
EXPECT_TRUE(file.seek(20, File::Origin::Current));
EXPECT_EQ((ssize_t)file.size(), file.offset());
file.close();
}
TEST(File, MemoryWarmup) {
// Test with null address - should not crash
File::MemoryWarmup(nullptr, 1024);
// Test with zero length - should not crash
char buf[1];
File::MemoryWarmup(buf, 0);
// Test with a small buffer
{
const size_t size = 4096;
std::vector<uint8_t> buffer(size, 0xAA);
File::MemoryWarmup(buffer.data(), buffer.size());
// Verify buffer is still intact after warmup
EXPECT_EQ(buffer[0], 0xAA);
EXPECT_EQ(buffer[size - 1], 0xAA);
}
// Test with a large buffer spanning multiple pages
{
const size_t size = MemoryHelper::PageSize() * 16 + 100;
std::vector<uint8_t> buffer(size, 0x55);
File::MemoryWarmup(buffer.data(), buffer.size());
EXPECT_EQ(buffer[0], 0x55);
EXPECT_EQ(buffer[size - 1], 0x55);
}
// Test with memory-mapped file
{
const char *file_path = "file_warmup_testing.tmp";
size_t file_size = MemoryHelper::PageSize() * 4;
File file;
ASSERT_TRUE(file.create(file_path, file_size));
void *addr = file.map(0, file_size, File::MMAP_SHARED);
ASSERT_TRUE(addr != nullptr);
// Fill with data
memset(addr, 0x77, file_size);
EXPECT_TRUE(File::MemoryFlush(addr, file_size));
File::MemoryUnmap(addr, file_size);
file.close();
// Open read-only and warmup
ASSERT_TRUE(file.open(file_path, true));
addr = file.map(0, file_size, 0);
ASSERT_TRUE(addr != nullptr);
File::MemoryWarmup(addr, file_size);
// Verify data is accessible after warmup
uint8_t *p = reinterpret_cast<uint8_t *>(addr);
EXPECT_EQ(p[0], 0x77);
EXPECT_EQ(p[file_size - 1], 0x77);
File::MemoryUnmap(addr, file_size);
file.close();
File::Delete(file_path);
}
}