winfsp unit tests and fixes
Some checks failed
BlockStorage/repertory/pipeline/head There was a failure building this commit
Some checks failed
BlockStorage/repertory/pipeline/head There was a failure building this commit
This commit is contained in:
parent
04daf393d7
commit
4c3d759837
@ -30,7 +30,7 @@
|
|||||||
namespace repertory {
|
namespace repertory {
|
||||||
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
|
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
|
||||||
|
|
||||||
static void test_file(auto mount_location, auto &&file_path, auto &&flags) {
|
static void test_file(auto &&mount_location, auto &&file_path, auto &&flags) {
|
||||||
SYSTEM_INFO sys_info{};
|
SYSTEM_INFO sys_info{};
|
||||||
::GetSystemInfo(&sys_info);
|
::GetSystemInfo(&sys_info);
|
||||||
|
|
||||||
@ -195,7 +195,7 @@ static void test_file(auto mount_location, auto &&file_path, auto &&flags) {
|
|||||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_append_file(auto mount_location, auto &&file_path,
|
static void test_append_file(auto &&mount_location, auto &&file_path,
|
||||||
auto &&flags, bool should_fail = false) {
|
auto &&flags, bool should_fail = false) {
|
||||||
SYSTEM_INFO sys_info{};
|
SYSTEM_INFO sys_info{};
|
||||||
::GetSystemInfo(&sys_info);
|
::GetSystemInfo(&sys_info);
|
||||||
@ -258,7 +258,7 @@ static void test_append_file(auto mount_location, auto &&file_path,
|
|||||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_overlapped_file(auto mount_location, auto &&file_path,
|
static void test_overlapped_file(auto &&mount_location, auto &&file_path,
|
||||||
auto &&flags) {
|
auto &&flags) {
|
||||||
SYSTEM_INFO sys_info{};
|
SYSTEM_INFO sys_info{};
|
||||||
::GetSystemInfo(&sys_info);
|
::GetSystemInfo(&sys_info);
|
||||||
@ -440,6 +440,306 @@ static void test_overlapped_file(auto mount_location, auto &&file_path,
|
|||||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_mixed_file(auto &&mount_location, auto &&file_path) {
|
||||||
|
SYSTEM_INFO sys_info{};
|
||||||
|
::GetSystemInfo(&sys_info);
|
||||||
|
|
||||||
|
DWORD bytes_per_sector{};
|
||||||
|
DWORD free_clusters{};
|
||||||
|
DWORD sectors_per_cluster{};
|
||||||
|
DWORD total_clusters{};
|
||||||
|
EXPECT_TRUE(::GetDiskFreeSpaceA(mount_location.c_str(), §ors_per_cluster,
|
||||||
|
&bytes_per_sector, &free_clusters,
|
||||||
|
&total_clusters));
|
||||||
|
const auto buffer_size = 16U * sys_info.dwPageSize;
|
||||||
|
auto write_buffer = utils::generate_secure_random<data_buffer>(buffer_size);
|
||||||
|
|
||||||
|
auto handle0 = CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||||
|
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
ASSERT_NE(INVALID_HANDLE_VALUE, handle0);
|
||||||
|
|
||||||
|
auto handle1 =
|
||||||
|
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, nullptr);
|
||||||
|
ASSERT_NE(INVALID_HANDLE_VALUE, handle0);
|
||||||
|
|
||||||
|
auto pointer = ::SetFilePointer(handle0, 0, 0, FILE_BEGIN);
|
||||||
|
EXPECT_EQ(0U, pointer);
|
||||||
|
|
||||||
|
DWORD bytes_written{};
|
||||||
|
EXPECT_TRUE(::WriteFile(handle0, write_buffer.data(), bytes_per_sector,
|
||||||
|
&bytes_written, nullptr));
|
||||||
|
|
||||||
|
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||||
|
EXPECT_EQ(pointer + bytes_per_sector,
|
||||||
|
::SetFilePointer(handle0, 0, 0, FILE_CURRENT));
|
||||||
|
|
||||||
|
pointer = ::SetFilePointer(handle1, 2 * bytes_per_sector, 0, FILE_BEGIN);
|
||||||
|
EXPECT_EQ(2U * bytes_per_sector, pointer);
|
||||||
|
|
||||||
|
EXPECT_TRUE(::WriteFile(handle1, write_buffer.data(), bytes_per_sector,
|
||||||
|
&bytes_written, nullptr));
|
||||||
|
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||||
|
EXPECT_EQ(pointer + bytes_written,
|
||||||
|
::SetFilePointer(handle1, 0, 0, FILE_CURRENT));
|
||||||
|
|
||||||
|
pointer = ::SetFilePointer(handle0, 0, 0, FILE_BEGIN);
|
||||||
|
EXPECT_EQ(0U, pointer);
|
||||||
|
|
||||||
|
data_buffer read_buffer{};
|
||||||
|
read_buffer.resize(buffer_size);
|
||||||
|
DWORD bytes_read{};
|
||||||
|
EXPECT_TRUE(::ReadFile(handle0, read_buffer.data(), bytes_per_sector,
|
||||||
|
&bytes_read, nullptr));
|
||||||
|
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||||
|
EXPECT_EQ(pointer + bytes_read,
|
||||||
|
::SetFilePointer(handle0, 0, 0, FILE_CURRENT));
|
||||||
|
EXPECT_EQ(0,
|
||||||
|
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||||
|
|
||||||
|
read_buffer.clear();
|
||||||
|
read_buffer.resize(buffer_size);
|
||||||
|
pointer = ::SetFilePointer(handle1, 0, 0, FILE_BEGIN);
|
||||||
|
EXPECT_EQ(0U, pointer);
|
||||||
|
EXPECT_TRUE(::ReadFile(handle1, read_buffer.data(), bytes_per_sector,
|
||||||
|
&bytes_read, nullptr));
|
||||||
|
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||||
|
EXPECT_EQ(pointer + bytes_read,
|
||||||
|
::SetFilePointer(handle1, 0, 0, FILE_CURRENT));
|
||||||
|
EXPECT_EQ(0,
|
||||||
|
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||||
|
|
||||||
|
EXPECT_TRUE(::CloseHandle(handle0));
|
||||||
|
|
||||||
|
EXPECT_TRUE(::CloseHandle(handle1));
|
||||||
|
|
||||||
|
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||||
|
|
||||||
|
handle0 = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||||
|
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
EXPECT_NE(INVALID_HANDLE_VALUE, handle0);
|
||||||
|
|
||||||
|
pointer = ::SetFilePointer(handle0, 0, 0, FILE_BEGIN);
|
||||||
|
EXPECT_EQ(0U, pointer);
|
||||||
|
EXPECT_TRUE(::WriteFile(handle0, write_buffer.data(), bytes_per_sector,
|
||||||
|
&bytes_written, nullptr));
|
||||||
|
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||||
|
EXPECT_EQ(pointer + bytes_written,
|
||||||
|
::SetFilePointer(handle0, 0, 0, FILE_CURRENT));
|
||||||
|
|
||||||
|
pointer = ::SetFilePointer(handle0, 2 * bytes_per_sector, 0, FILE_BEGIN);
|
||||||
|
EXPECT_EQ(2U * bytes_per_sector, pointer);
|
||||||
|
EXPECT_TRUE(::WriteFile(handle0, write_buffer.data(), bytes_per_sector,
|
||||||
|
&bytes_written, nullptr));
|
||||||
|
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||||
|
EXPECT_EQ(pointer + bytes_written,
|
||||||
|
::SetFilePointer(handle0, 0, 0, FILE_CURRENT));
|
||||||
|
|
||||||
|
pointer = ::SetFilePointer(handle0, 0, 0, FILE_BEGIN);
|
||||||
|
EXPECT_EQ(0U, pointer);
|
||||||
|
|
||||||
|
read_buffer.clear();
|
||||||
|
read_buffer.resize(buffer_size);
|
||||||
|
EXPECT_TRUE(::ReadFile(handle0, read_buffer.data(), bytes_per_sector,
|
||||||
|
&bytes_read, nullptr));
|
||||||
|
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||||
|
EXPECT_EQ(pointer + bytes_read,
|
||||||
|
::SetFilePointer(handle0, 0, 0, FILE_CURRENT));
|
||||||
|
EXPECT_EQ(0,
|
||||||
|
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||||
|
|
||||||
|
EXPECT_TRUE(::CloseHandle(handle0));
|
||||||
|
|
||||||
|
handle1 =
|
||||||
|
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, nullptr);
|
||||||
|
EXPECT_NE(INVALID_HANDLE_VALUE, handle0);
|
||||||
|
|
||||||
|
pointer = ::SetFilePointer(handle1, 0, 0, FILE_BEGIN);
|
||||||
|
EXPECT_EQ(0U, pointer);
|
||||||
|
|
||||||
|
read_buffer.clear();
|
||||||
|
read_buffer.resize(buffer_size);
|
||||||
|
EXPECT_TRUE(::ReadFile(handle1, read_buffer.data(), bytes_per_sector,
|
||||||
|
&bytes_read, nullptr));
|
||||||
|
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||||
|
EXPECT_EQ(pointer + bytes_read,
|
||||||
|
::SetFilePointer(handle1, 0, 0, FILE_CURRENT));
|
||||||
|
EXPECT_EQ(0,
|
||||||
|
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||||
|
|
||||||
|
EXPECT_TRUE(::CloseHandle(handle1));
|
||||||
|
|
||||||
|
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// static void test_mmap_file(auto &&mount_location, auto &&file_path,
|
||||||
|
// auto &&flags) {
|
||||||
|
// SYSTEM_INFO sys_info{};
|
||||||
|
// ::GetSystemInfo(&sys_info);
|
||||||
|
//
|
||||||
|
// DWORD bytes_per_sector{};
|
||||||
|
// DWORD free_clusters{};
|
||||||
|
// DWORD sectors_per_cluster{};
|
||||||
|
// DWORD total_clusters{};
|
||||||
|
// EXPECT_TRUE(::GetDiskFreeSpaceA(mount_location.c_str(),
|
||||||
|
// §ors_per_cluster,
|
||||||
|
// &bytes_per_sector, &free_clusters,
|
||||||
|
// &total_clusters));
|
||||||
|
// const auto buffer_size = 16U * sys_info.dwPageSize;
|
||||||
|
// auto write_buffer =
|
||||||
|
// utils::generate_secure_random<data_buffer>(buffer_size);
|
||||||
|
//
|
||||||
|
// handle1 = CreateFileW(FilePath, GENERIC_READ | GENERIC_WRITE,
|
||||||
|
// FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_ALWAYS,
|
||||||
|
// FILE_ATTRIBUTE_NORMAL | CreateFlags, 0);
|
||||||
|
// ASSERT(INVALID_HANDLE_VALUE != handle1);
|
||||||
|
//
|
||||||
|
// Mapping = CreateFileMappingW(handle1, 0, PAGE_READWRITE, 0,
|
||||||
|
// FileSize0 + FileSize1, 0);
|
||||||
|
// ASSERT(0 != Mapping);
|
||||||
|
//
|
||||||
|
// if (EarlyClose) {
|
||||||
|
// Success = CloseHandle(Handle);
|
||||||
|
// ASSERT(Success);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// MappedView = MapViewOfFile(Mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||||
|
// ASSERT(0 != MappedView);
|
||||||
|
//
|
||||||
|
// srand(seed);
|
||||||
|
// for (PUINT8 Bgn = MappedView + FileSize1 / 2, End = Bgn + FileSize0;
|
||||||
|
// End > Bgn; Bgn++)
|
||||||
|
// *Bgn = rand() & 0xff;
|
||||||
|
//
|
||||||
|
// Success = UnmapViewOfFile(MappedView);
|
||||||
|
// ASSERT(Success);
|
||||||
|
//
|
||||||
|
// MappedView = MapViewOfFile(Mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||||
|
// ASSERT(0 != MappedView);
|
||||||
|
//
|
||||||
|
// srand(seed);
|
||||||
|
// for (PUINT8 Bgn = MappedView + FileSize1 / 2, End = Bgn + FileSize0;
|
||||||
|
// End > Bgn; Bgn++)
|
||||||
|
// ASSERT(*Bgn == (rand() & 0xff));
|
||||||
|
//
|
||||||
|
// Success = UnmapViewOfFile(MappedView);
|
||||||
|
// ASSERT(Success);
|
||||||
|
//
|
||||||
|
// Success = CloseHandle(Mapping);
|
||||||
|
// ASSERT(Success);
|
||||||
|
//
|
||||||
|
// if (!EarlyClose) {
|
||||||
|
// Success = CloseHandle(Handle);
|
||||||
|
// ASSERT(Success);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// handle1 = CreateFileW(FilePath, GENERIC_READ | GENERIC_WRITE,
|
||||||
|
// FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
|
||||||
|
// FILE_ATTRIBUTE_NORMAL | CreateFlags, 0);
|
||||||
|
// ASSERT(INVALID_HANDLE_VALUE != handle1);
|
||||||
|
//
|
||||||
|
// Mapping = CreateFileMappingW(handle1, 0, PAGE_READWRITE, 0,
|
||||||
|
// FileSize0 + FileSize1, 0);
|
||||||
|
// ASSERT(0 != Mapping);
|
||||||
|
//
|
||||||
|
// if (EarlyClose) {
|
||||||
|
// Success = CloseHandle(Handle);
|
||||||
|
// ASSERT(Success);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// MappedView = MapViewOfFile(Mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||||
|
// ASSERT(0 != MappedView);
|
||||||
|
//
|
||||||
|
// srand(seed);
|
||||||
|
// for (PUINT8 Bgn = MappedView + FileSize1 / 2, End = Bgn + FileSize0;
|
||||||
|
// End > Bgn; Bgn++)
|
||||||
|
// ASSERT(*Bgn == (rand() & 0xff));
|
||||||
|
//
|
||||||
|
// if (!EarlyClose) {
|
||||||
|
// pointer = SetFilePointer(handle1, FileSize0 / 2, 0, FILE_BEGIN);
|
||||||
|
// ASSERT(FileSize0 / 2 == pointer);
|
||||||
|
// Success =
|
||||||
|
// WriteFile(handle1, Buffer[0], bytes_per_sector, &BytesTransferred, 0);
|
||||||
|
// ASSERT(Success);
|
||||||
|
// ASSERT(bytes_per_sector == BytesTransferred);
|
||||||
|
// ASSERT(pointer + BytesTransferred ==
|
||||||
|
// SetFilePointer(handle1, 0, 0, FILE_CURRENT));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Success = UnmapViewOfFile(MappedView);
|
||||||
|
// ASSERT(Success);
|
||||||
|
//
|
||||||
|
// Success = CloseHandle(Mapping);
|
||||||
|
// ASSERT(Success);
|
||||||
|
//
|
||||||
|
// if (!EarlyClose) {
|
||||||
|
// Success = CloseHandle(Handle);
|
||||||
|
// ASSERT(Success);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// handle1 = CreateFileW(FilePath, GENERIC_READ | GENERIC_WRITE,
|
||||||
|
// FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
|
||||||
|
// FILE_ATTRIBUTE_NORMAL | CreateFlags, 0);
|
||||||
|
// ASSERT(INVALID_HANDLE_VALUE != handle1);
|
||||||
|
//
|
||||||
|
// Mapping = CreateFileMappingW(handle1, 0, PAGE_READWRITE, 0,
|
||||||
|
// FileSize0 + FileSize1, 0);
|
||||||
|
// ASSERT(0 != Mapping);
|
||||||
|
//
|
||||||
|
// if (EarlyClose) {
|
||||||
|
// Success = CloseHandle(Handle);
|
||||||
|
// ASSERT(Success);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// MappedView = MapViewOfFile(Mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||||
|
// ASSERT(0 != MappedView);
|
||||||
|
//
|
||||||
|
// if (!EarlyClose) {
|
||||||
|
// pointer = SetFilePointer(handle1, FileSize0 / 2, 0, FILE_BEGIN);
|
||||||
|
// ASSERT(FileSize0 / 2 == pointer);
|
||||||
|
// memset(Buffer[1], 0, bytes_per_sector);
|
||||||
|
// Success = ReadFile(handle1, Buffer[1], bytes_per_sector, &BytesTransferred,
|
||||||
|
// 0); ASSERT(Success); ASSERT(bytes_per_sector == BytesTransferred);
|
||||||
|
// ASSERT(pointer + BytesTransferred ==
|
||||||
|
// SetFilePointer(handle1, 0, 0, FILE_CURRENT));
|
||||||
|
// ASSERT(0 == memcmp(Buffer[0], Buffer[1], BytesTransferred));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// srand(seed);
|
||||||
|
// for (PUINT8 Bgn = MappedView + FileSize1 / 2,
|
||||||
|
// End = MappedView + FileSize0 / 2;
|
||||||
|
// End > Bgn; Bgn++)
|
||||||
|
// ASSERT(*Bgn == (rand() & 0xff));
|
||||||
|
// if (!EarlyClose)
|
||||||
|
// ASSERT(0 == memcmp(Buffer[0], MappedView + FileSize0 / 2,
|
||||||
|
// bytes_per_sector));
|
||||||
|
// for (size_t i = 0; bytes_per_sector > i; i++)
|
||||||
|
// rand();
|
||||||
|
// for (PUINT8 Bgn = MappedView + FileSize0 / 2 + bytes_per_sector,
|
||||||
|
// End = MappedView + FileSize1 / 2 + FileSize0;
|
||||||
|
// End > Bgn; Bgn++)
|
||||||
|
// ASSERT(*Bgn == (rand() & 0xff));
|
||||||
|
//
|
||||||
|
// Success = UnmapViewOfFile(MappedView);
|
||||||
|
// ASSERT(Success);
|
||||||
|
//
|
||||||
|
// Success = CloseHandle(Mapping);
|
||||||
|
// ASSERT(Success);
|
||||||
|
//
|
||||||
|
// if (!EarlyClose) {
|
||||||
|
// Success = CloseHandle(Handle);
|
||||||
|
// ASSERT(Success);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Success = DeleteFileW(FilePath);
|
||||||
|
// ASSERT(Success);
|
||||||
|
// }
|
||||||
|
|
||||||
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_no_flags) {
|
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_no_flags) {
|
||||||
auto file_path{
|
auto file_path{
|
||||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||||
@ -504,6 +804,34 @@ TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_overlapped_write_through) {
|
|||||||
test_overlapped_file(this->mount_location, file_path,
|
test_overlapped_file(this->mount_location, file_path,
|
||||||
FILE_FLAG_WRITE_THROUGH);
|
FILE_FLAG_WRITE_THROUGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TYPED_TEST(winfsp_test, rdrw_can_read_and_write_mmap_file_no_flags) {
|
||||||
|
// auto file_path{
|
||||||
|
// utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||||
|
// };
|
||||||
|
// test_mmap_file(this->mount_location, file_path, 0U);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// TYPED_TEST(winfsp_test, rdrw_can_read_and_write_mmap_file_no_buffering) {
|
||||||
|
// auto file_path{
|
||||||
|
// utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||||
|
// };
|
||||||
|
// test_mmap_file(this->mount_location, file_path, FILE_FLAG_NO_BUFFERING);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_mmap_write_through) {
|
||||||
|
// auto file_path{
|
||||||
|
// utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||||
|
// };
|
||||||
|
// test_mmap_file(this->mount_location, file_path, FILE_FLAG_WRITE_THROUGH);
|
||||||
|
// }
|
||||||
|
|
||||||
|
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_mixed_file) {
|
||||||
|
auto file_path{
|
||||||
|
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||||
|
};
|
||||||
|
test_mixed_file(this->mount_location, file_path);
|
||||||
|
}
|
||||||
} // namespace repertory
|
} // namespace repertory
|
||||||
|
|
||||||
#endif // defined(_WIN32)
|
#endif // defined(_WIN32)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user