2 * Ceph - scalable distributed file system
4 * Copyright (C) 2022 Cloudbase Solutions
6 * This is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License version 2.1, as published by the Free Software
9 * Foundation. See file COPYING.
17 #include <sys/socket.h>
20 #include "gtest/gtest.h"
22 #include "common/SubProcess.h"
23 #include "common/run_cmd.h"
24 #include "include/uuid.h"
26 #define DEFAULT_MOUNTPOINT "X:\\"
27 #define MOUNT_POLL_ATTEMPT 10
28 #define MOUNT_POLL_INTERVAL_MS 1000
29 #define TEST_VOL_SERIAL "1234567890"
32 namespace fs
= std::filesystem
;
33 using namespace std::chrono_literals
;
35 std::string
get_uuid() {
37 suffix
.generate_random();
39 return suffix
.to_string();
42 bool move_eof(HANDLE handle
, LARGE_INTEGER offset
) {
44 // Move file pointer to FILE_BEGIN + offset
45 if (!SetFilePointerEx(handle
, offset
, NULL
, FILE_BEGIN
)) {
46 std::cerr
<< "Setting file pointer failed. err: "
47 << GetLastError() << std::endl
;
51 if (!SetEndOfFile(handle
)) {
52 std::cerr
<< "Setting EOF failed. err: " << GetLastError() << std::endl
;
59 void write_file(std::string file_path
, std::string data
) {
63 ASSERT_TRUE(file
.is_open())
64 << "Failed to open file: " << file_path
;
71 void expect_write_failure(std::string file_path
) {
75 ASSERT_FALSE(file
.is_open());
78 std::string
read_file(std::string file_path
) {
81 std::string
content((std::istreambuf_iterator
<char>(file
)),
82 std::istreambuf_iterator
<char>());
88 void check_write_file(std::string file_path
, std::string data
) {
89 write_file(file_path
, data
);
90 ASSERT_EQ(read_file(file_path
), data
);
93 int wait_for_mount(std::string mount_path
) {
94 std::cerr
<< "Waiting for mount: " << mount_path
<< std::endl
;
99 if (attempts
< MOUNT_POLL_ATTEMPT
)
100 Sleep(MOUNT_POLL_INTERVAL_MS
);
101 } while (!fs::exists(mount_path
)
102 && attempts
< MOUNT_POLL_ATTEMPT
);
104 if (!fs::exists(mount_path
)) {
105 std::cerr
<< "Timed out waiting for ceph-dokan mount: "
106 << mount_path
<< std::endl
;
110 std::cerr
<< "Successfully mounted: " << mount_path
<< std::endl
;
115 void map_dokan(SubProcess
** mount
, const char* mountpoint
) {
116 SubProcess
* new_mount
= new SubProcess("ceph-dokan");
118 new_mount
->add_cmd_args("map", "--win-vol-name", "TestCeph",
119 "--win-vol-serial", TEST_VOL_SERIAL
,
120 "-l", mountpoint
, NULL
);
123 ASSERT_EQ(new_mount
->spawn(), 0);
124 ASSERT_EQ(wait_for_mount(mountpoint
), 0);
127 void map_dokan_read_only(
129 const char* mountpoint
131 SubProcess
* new_mount
= new SubProcess("ceph-dokan");
132 new_mount
->add_cmd_args("map", "--win-vol-name", "TestCeph",
133 "--win-vol-serial", TEST_VOL_SERIAL
,
134 "--read-only", "-l", mountpoint
, NULL
);
137 ASSERT_EQ(new_mount
->spawn(), 0);
138 ASSERT_EQ(wait_for_mount(mountpoint
), 0);
139 std::cerr
<< mountpoint
<< " mounted in read-only mode"
143 void map_dokan_with_maxpath(
145 const char* mountpoint
,
146 uint64_t max_path_len
)
148 SubProcess
* new_mount
= new SubProcess("ceph-dokan");
149 new_mount
->add_cmd_args("map", "--debug", "--dokan-stderr",
150 "--win-vol-name", "TestCeph",
151 "--win-vol-serial", TEST_VOL_SERIAL
,
153 (std::to_string(max_path_len
)).c_str(),
154 "-l", mountpoint
, NULL
);
157 ASSERT_EQ(new_mount
->spawn(), 0);
158 if (256 <= max_path_len
&& max_path_len
<= 4096) {
159 ASSERT_EQ(wait_for_mount(mountpoint
), 0);
161 ASSERT_NE(wait_for_mount(mountpoint
), 0);
165 void unmap_dokan(SubProcess
* mount
, const char* mountpoint
) {
166 std::string ret
= run_cmd("ceph-dokan", "unmap", "-l",
167 mountpoint
, (char*)NULL
);
169 ASSERT_EQ(ret
, "") << "Failed unmapping: " << mountpoint
;
170 std::cerr
<< "Unmounted: " << mountpoint
<< std::endl
;
172 ASSERT_EQ(mount
->join(), 0);
175 int get_volume_max_path(std::string mountpoint
){
176 char volume_name
[MAX_PATH
+ 1] = { 0 };
177 char file_system_name
[MAX_PATH
+ 1] = { 0 };
178 DWORD serial_number
= 0;
179 DWORD max_component_len
= 0;
180 DWORD file_system_flags
= 0;
181 if (GetVolumeInformation(
189 sizeof(file_system_name
)) != TRUE
) {
190 std::cerr
<< "GetVolumeInformation() failed, error: "
191 << GetLastError() << std::endl
;
194 return max_component_len
;
197 static SubProcess
* shared_mount
= nullptr;
199 class DokanTests
: public testing::Test
203 static void SetUpTestSuite() {
204 map_dokan(&shared_mount
, DEFAULT_MOUNTPOINT
);
207 static void TearDownTestSuite() {
209 unmap_dokan(shared_mount
, DEFAULT_MOUNTPOINT
);
211 shared_mount
= nullptr;
215 TEST_F(DokanTests
, test_mount
) {
216 std::string mountpoint
= "Y:\\";
217 SubProcess
* mount
= nullptr;
218 map_dokan(&mount
, mountpoint
.c_str());
219 unmap_dokan(mount
, mountpoint
.c_str());
222 TEST_F(DokanTests
, test_mount_read_only
) {
223 std::string mountpoint
= "Z:\\";
224 std::string data
= "abc123";
225 std::string success_file_path
= "ro_success_" + get_uuid();
226 std::string failed_file_path
= "ro_fail_" + get_uuid();
228 SubProcess
* mount
= nullptr;
229 map_dokan(&mount
, mountpoint
.c_str());
231 check_write_file(mountpoint
+ success_file_path
, data
);
232 ASSERT_TRUE(fs::exists(mountpoint
+ success_file_path
));
234 unmap_dokan(mount
, mountpoint
.c_str());
237 map_dokan_read_only(&mount
, mountpoint
.c_str());
239 expect_write_failure(mountpoint
+ failed_file_path
);
240 ASSERT_FALSE(fs::exists(mountpoint
+ failed_file_path
));
242 ASSERT_TRUE(fs::exists(mountpoint
+ success_file_path
));
243 ASSERT_EQ(read_file(mountpoint
+ success_file_path
), data
);
245 std::string
exception_msg(
246 "filesystem error: cannot remove: No such device ["
247 + mountpoint
+ success_file_path
+ "]");
250 fs::remove(mountpoint
+ success_file_path
);
251 } catch(const fs::filesystem_error
&e
) {
252 EXPECT_STREQ(e
.what(), exception_msg
.c_str());
255 }, fs::filesystem_error
);
256 unmap_dokan(mount
, mountpoint
.c_str());
258 map_dokan(&mount
, mountpoint
.c_str());
259 ASSERT_TRUE(fs::exists(mountpoint
+ success_file_path
));
260 ASSERT_TRUE(fs::remove(mountpoint
+ success_file_path
));
261 unmap_dokan(mount
, mountpoint
.c_str());
264 TEST_F(DokanTests
, test_delete_on_close
) {
265 std::string file_path
= DEFAULT_MOUNTPOINT
"file_" + get_uuid();
266 HANDLE hFile
= CreateFile(
268 GENERIC_WRITE
, // open for writing
269 0, // sharing mode, none in this case
270 0, // use default security descriptor
272 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_DELETE_ON_CLOSE
,
275 ASSERT_NE(hFile
, INVALID_HANDLE_VALUE
)
276 << "Could not open file: "
277 << DEFAULT_MOUNTPOINT
"test_create.txt "
278 << "err: " << GetLastError() << std::endl
;
280 ASSERT_NE(CloseHandle(hFile
), 0);
282 // FILE_FLAG_DELETE_ON_CLOSE is used
283 ASSERT_FALSE(fs::exists(file_path
));
286 TEST_F(DokanTests
, test_io
) {
287 std::string data
= "abcdef";
288 std::string file_path
= "test_io_" + get_uuid();
290 std::string mountpoint
= "I:\\";
291 SubProcess
* mount
= nullptr;
292 map_dokan(&mount
, mountpoint
.c_str());
294 check_write_file(mountpoint
+ file_path
, data
);
295 ASSERT_TRUE(fs::exists(mountpoint
+ file_path
));
296 unmap_dokan(mount
, mountpoint
.c_str());
300 map_dokan(&mount
, mountpoint
.c_str());
302 ASSERT_TRUE(fs::exists(mountpoint
+ file_path
));
303 EXPECT_EQ(data
, read_file(mountpoint
+ file_path
));
304 ASSERT_TRUE(fs::remove((mountpoint
+ file_path
).c_str()));
305 ASSERT_FALSE(fs::exists(mountpoint
+ file_path
));
306 unmap_dokan(mount
, mountpoint
.c_str());
309 TEST_F(DokanTests
, test_subfolders
) {
310 std::string base_dir_path
= DEFAULT_MOUNTPOINT
"base_dir_"
312 std::string sub_dir_path
= base_dir_path
313 + "test_sub_dir" + get_uuid();
314 std::string base_dir_file
= base_dir_path
315 + "file_" + get_uuid();
316 std::string sub_dir_file
= sub_dir_path
317 + "file_" + get_uuid();
319 std::string data
= "abc";
321 ASSERT_EQ(fs::create_directory(base_dir_path
), true);
322 ASSERT_TRUE(fs::exists(base_dir_path
));
324 ASSERT_EQ(fs::create_directory(sub_dir_path
), true);
325 ASSERT_TRUE(fs::exists(sub_dir_path
));
327 check_write_file(base_dir_file
, data
);
328 ASSERT_TRUE(fs::exists(base_dir_file
));
330 check_write_file(sub_dir_file
, data
);
331 ASSERT_TRUE(fs::exists(sub_dir_file
));
333 ASSERT_TRUE(fs::remove((sub_dir_file
).c_str()))
334 << "Failed to remove file: " << sub_dir_file
;
335 ASSERT_FALSE(fs::exists(sub_dir_file
));
338 ASSERT_TRUE(fs::remove((sub_dir_path
).c_str()))
339 << "Failed to remove directory: " << sub_dir_path
;
340 ASSERT_FALSE(fs::exists(sub_dir_file
));
342 ASSERT_NE(fs::remove_all((base_dir_path
).c_str()), 0)
343 << "Failed to remove directory: " << base_dir_path
;
344 ASSERT_FALSE(fs::exists(sub_dir_file
));
347 TEST_F(DokanTests
, test_find_files
) {
348 std::string basedir_path
= "X:/find_" + get_uuid();
349 std::string subdir_path
= basedir_path
+ "/dir_" + get_uuid();
350 std::string file1_path
= basedir_path
+ "/file1_" + get_uuid();
351 std::string file2_path
= subdir_path
+ "/file2_" + get_uuid();
354 fs::create_directories(subdir_path
)
357 std::ofstream
{file1_path
};
358 std::ofstream
{file2_path
};
360 std::vector
<std::string
> paths
;
362 for (const auto & entry
:
363 fs::recursive_directory_iterator(basedir_path
)
365 paths
.push_back(entry
.path().generic_string());
368 ASSERT_NE(std::find(begin(paths
), end(paths
), subdir_path
), end(paths
));
369 ASSERT_NE(std::find(begin(paths
), end(paths
), file1_path
), end(paths
));
370 ASSERT_NE(std::find(begin(paths
), end(paths
), file2_path
), end(paths
));
373 ASSERT_NE(fs::remove_all(basedir_path
), 0);
376 TEST_F(DokanTests
, test_move_file
) {
377 std::string dir1_path
= DEFAULT_MOUNTPOINT
378 "test_mv_1_" + get_uuid() + "\\";
379 std::string dir2_path
= DEFAULT_MOUNTPOINT
380 "test_mv_2_" + get_uuid() + "\\";
381 std::string file_name
= "mv_file_" + get_uuid();
382 std::string data
= "abcd";
384 ASSERT_TRUE(fs::create_directory(dir1_path
));
385 ASSERT_TRUE(fs::create_directory(dir2_path
));
387 check_write_file(dir1_path
+ file_name
, data
);
389 fs::rename(dir1_path
+ file_name
, dir2_path
+ file_name
);
391 ASSERT_TRUE(fs::exists(dir2_path
+ file_name
));
392 ASSERT_FALSE(fs::exists(dir1_path
+ file_name
));
394 ASSERT_EQ(data
, read_file(dir2_path
+ file_name
));
397 ASSERT_NE(fs::remove_all(dir1_path
),0);
398 ASSERT_NE(fs::remove_all(dir2_path
),0);
401 TEST_F(DokanTests
, test_max_path
) {
402 std::string mountpoint
= "P:\\";
403 std::string extended_mountpoint
= "\\\\?\\" + mountpoint
;
404 SubProcess
* mount
= nullptr;
405 char dir
[200] = { 0 };
406 char file
[200] = { 0 };
407 std::string data
= "abcd1234";
409 memset(dir
, 'd', sizeof(dir
) - 1);
410 memset(file
, 'f', sizeof(file
) - 1);
412 uint64_t max_path_len
= 4096;
414 map_dokan_with_maxpath(&mount
,
417 EXPECT_EQ(get_volume_max_path(extended_mountpoint
),
420 std::string long_dir_path
= extended_mountpoint
;
422 std::string dir_names
[15];
424 for (int i
= 0; i
< 15; i
++) {
425 std::string crt_dir
= std::string(dir
) + "_"
427 long_dir_path
.append(crt_dir
);
428 int stat
= _mkdir(long_dir_path
.c_str());
429 ASSERT_EQ(stat
, 0) << "Error creating directory " << i
430 << ": " << GetLastError() << std::endl
;
431 dir_names
[i
] = crt_dir
;
433 std::string file_path
= long_dir_path
+ "\\" + std::string(file
)
436 check_write_file(file_path
, data
);
439 // fs::remove is unable to handle long Windows paths
440 EXPECT_NE(DeleteFileA(file_path
.c_str()), 0);
442 for (int i
= 14; i
>= 0; i
--) {
443 std::string remove_dir
= extended_mountpoint
;
444 for (int j
= 0; j
<= i
; j
++) {
445 remove_dir
.append(dir_names
[j
]);
448 EXPECT_NE(RemoveDirectoryA(remove_dir
.c_str()), 0);
451 unmap_dokan(mount
, mountpoint
.c_str());
453 // value exceeds 32767, so a failure is expected
454 max_path_len
= 32770;
455 map_dokan_with_maxpath(&mount
,
458 ASSERT_FALSE(fs::exists(mountpoint
));
460 // value is below 256, so a failure is expected
462 map_dokan_with_maxpath(&mount
,
465 ASSERT_FALSE(fs::exists(mountpoint
));
468 map_dokan(&mount
, mountpoint
.c_str());
469 EXPECT_EQ(get_volume_max_path(mountpoint
.c_str()), 256);
471 unmap_dokan(mount
, mountpoint
.c_str());
474 TEST_F(DokanTests
, test_set_eof
) {
475 std::string file_path
= DEFAULT_MOUNTPOINT
"test_eof_"
477 HANDLE hFile
= CreateFile(
479 GENERIC_WRITE
, // open for writing
480 0, // sharing mode, none in this case
481 0, // use default security descriptor
483 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_DELETE_ON_CLOSE
,
486 ASSERT_NE(hFile
, INVALID_HANDLE_VALUE
)
487 << "Could not open file: "
488 << DEFAULT_MOUNTPOINT
"test_create.txt "
489 << "err: " << GetLastError() << std::endl
;
491 LARGE_INTEGER offset
;
492 offset
.QuadPart
= 2 * MByte
; // 2MB
494 LARGE_INTEGER file_size
;
496 ASSERT_TRUE(move_eof(hFile
, offset
));
497 ASSERT_NE(GetFileSizeEx(hFile
, &file_size
), 0);
498 EXPECT_EQ(file_size
.QuadPart
, offset
.QuadPart
);
500 offset
.QuadPart
= MByte
; // 1MB
502 ASSERT_TRUE(move_eof(hFile
, offset
));
503 ASSERT_NE(GetFileSizeEx(hFile
, &file_size
), 0);
504 EXPECT_EQ(file_size
.QuadPart
, offset
.QuadPart
);
506 ASSERT_NE(CloseHandle(hFile
), 0);
508 // FILE_FLAG_DELETE_ON_CLOSE is used
509 ASSERT_FALSE(fs::exists(file_path
));
512 TEST_F(DokanTests
, test_set_alloc_size
) {
513 std::string file_path
= DEFAULT_MOUNTPOINT
"test_alloc_size_"
515 HANDLE hFile
= CreateFile(
517 GENERIC_WRITE
, // open for writing
518 0, // sharing mode, none in this case
519 0, // use default security descriptor
521 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_DELETE_ON_CLOSE
,
524 ASSERT_NE(hFile
, INVALID_HANDLE_VALUE
)
525 << "Could not open file: "
526 << DEFAULT_MOUNTPOINT
"test_create.txt "
527 << "err: " << GetLastError() << std::endl
;
531 FILE_ALLOCATION_INFO fai
;
532 fai
.AllocationSize
= li
;
534 ASSERT_NE(SetFileInformationByHandle(
538 sizeof(FILE_ALLOCATION_INFO
)
539 ),0) << "Error: " << GetLastError();
541 LARGE_INTEGER offset
;
542 offset
.QuadPart
= 2 * MByte
;
543 LARGE_INTEGER file_size
;
545 ASSERT_TRUE(move_eof(hFile
, offset
));
546 ASSERT_NE(GetFileSizeEx(hFile
, &file_size
), 0);
547 EXPECT_EQ(file_size
.QuadPart
, offset
.QuadPart
);
549 offset
.QuadPart
= MByte
;
551 ASSERT_TRUE(move_eof(hFile
, offset
));
552 ASSERT_NE(GetFileSizeEx(hFile
, &file_size
), 0);
553 EXPECT_EQ(file_size
.QuadPart
, offset
.QuadPart
);
555 ASSERT_NE(CloseHandle(hFile
), 0);
557 // FILE_FLAG_DELETE_ON_CLOSE is used
558 ASSERT_FALSE(fs::exists(file_path
));
561 TEST_F(DokanTests
, test_file_type
) {
562 std::string test_dir
= DEFAULT_MOUNTPOINT
"test_info_"
564 std::string file_path
= test_dir
+ "file_"
566 std::string dir_path
= test_dir
+ "dir_"
569 ASSERT_TRUE(fs::create_directory(test_dir
));
571 std::ofstream
{file_path
};
572 ASSERT_TRUE(fs::create_directory(dir_path
));
574 ASSERT_TRUE(fs::is_regular_file(fs::status(file_path
)));
575 ASSERT_TRUE(fs::is_directory(fs::status(dir_path
)));
578 fs::remove_all(test_dir
);
582 TEST_F(DokanTests
, test_volume_info
) {
583 char volume_name
[MAX_PATH
+ 1] = { 0 };
584 char file_system_name
[MAX_PATH
+ 1] = { 0 };
585 DWORD serial_number
= 0;
586 DWORD max_component_len
= 0;
587 DWORD file_system_flags
= 0;
590 GetVolumeInformation(
598 sizeof(file_system_name
)),TRUE
)
599 << "GetVolumeInformation() failed, error: "
600 << GetLastError() << std::endl
;
602 ASSERT_STREQ(volume_name
, "TestCeph")
603 << "Received: " << volume_name
<< std::endl
;
604 ASSERT_STREQ(file_system_name
, "Ceph")
605 << "Received: " << file_system_name
<< std::endl
;
606 ASSERT_EQ(max_component_len
, 256);
607 ASSERT_EQ(serial_number
, std::stoi(TEST_VOL_SERIAL
))
608 << "Received: " << serial_number
<< std::endl
;
610 // Consider adding specific flags
611 // and check for them
612 // ASSERT_EQ(file_system_flags, 271);
615 TEST_F(DokanTests
, test_get_free_space
) {
617 const std::filesystem::space_info si
=
618 std::filesystem::space(DEFAULT_MOUNTPOINT
, ec
);
619 ASSERT_EQ(ec
.value(), 0);
621 ASSERT_NE(static_cast<std::intmax_t>(si
.capacity
), 0);
622 ASSERT_NE(static_cast<std::intmax_t>(si
.free
), 0);
623 ASSERT_NE(static_cast<std::intmax_t>(si
.available
), 0);
626 TEST_F(DokanTests
, test_file_timestamp
) {
627 std::string file1
= DEFAULT_MOUNTPOINT
"test_time1_"
629 std::string file2
= DEFAULT_MOUNTPOINT
"test_time2_"
631 std::string file3
= DEFAULT_MOUNTPOINT
"test_time3_"
634 std::ofstream
{file1
};
636 std::ofstream
{file2
};
638 std::ofstream
{file3
};
640 int64_t file1_creation
= fs::last_write_time(file1
)
641 .time_since_epoch().count();
642 int64_t file2_creation
= fs::last_write_time(file2
)
643 .time_since_epoch().count();
644 int64_t file3_creation
= fs::last_write_time(file3
)
645 .time_since_epoch().count();
647 EXPECT_LT(file1_creation
, file2_creation
);
648 EXPECT_LT(file2_creation
, file3_creation
);
650 // add 1h to file 1 creation time
651 fs::file_time_type file1_time
= fs::last_write_time(file1
);
653 fs::last_write_time(file1
, file1_time
+ 1h
);
655 int64_t file1_new_time
= fs::last_write_time(file1
)
656 .time_since_epoch().count();
658 EXPECT_EQ((file1_time
+ 1h
).time_since_epoch().count(),
660 EXPECT_GT(file1_new_time
, file2_creation
);
661 EXPECT_GT(file1_new_time
, file3_creation
);
663 ASSERT_TRUE(fs::remove(file1
));
664 ASSERT_TRUE(fs::remove(file2
));
665 ASSERT_TRUE(fs::remove(file3
));
668 TEST_F(DokanTests
, test_delete_disposition
) {
669 std::string file_path
= DEFAULT_MOUNTPOINT
"test_disp_"
671 HANDLE hFile
= CreateFile(file_path
.c_str(),
672 GENERIC_ALL
, // required for delete
673 0, // exclusive access
679 ASSERT_NE(hFile
, INVALID_HANDLE_VALUE
)
680 << "Could not open file: " << file_path
681 << "err: " << GetLastError() << std::endl
;
683 FILE_DISPOSITION_INFO fdi
;
684 fdi
.DeleteFile
= TRUE
; // marking for deletion
687 SetFileInformationByHandle(
691 sizeof(FILE_DISPOSITION_INFO
)), 0);
693 ASSERT_NE(CloseHandle(hFile
), 0);
694 ASSERT_FALSE(fs::exists(file_path
));
697 bool check_create_disposition(std::string path
, DWORD disposition
) {
698 HANDLE hFile
= CreateFile(path
.c_str(),
700 0, // exclusive access
706 if(hFile
== INVALID_HANDLE_VALUE
) {
710 if(CloseHandle(hFile
) == 0) {
717 TEST_F(DokanTests
, test_create_dispositions
) {
718 std::string file_path
= DEFAULT_MOUNTPOINT
"test_create_"
720 std::string non_existant_file
= DEFAULT_MOUNTPOINT
721 "test_create_" + get_uuid();
724 check_create_disposition(file_path
, CREATE_NEW
));
726 // CREATE_ALWAYS with existing file
728 check_create_disposition(file_path
, CREATE_ALWAYS
));
729 EXPECT_EQ(GetLastError(), ERROR_ALREADY_EXISTS
);
731 // CREATE_NEW with existing file
733 check_create_disposition(file_path
, CREATE_NEW
));
734 EXPECT_EQ(GetLastError(), ERROR_FILE_EXISTS
);
736 // OPEN_EXISTING with existing file
738 check_create_disposition(file_path
, OPEN_EXISTING
));
740 ASSERT_FALSE(fs::exists(non_existant_file
));
741 // OPEN_EXISTING with non-existant file
743 check_create_disposition(non_existant_file
, OPEN_EXISTING
));
744 EXPECT_EQ(GetLastError(), ERROR_FILE_NOT_FOUND
);
746 // OPEN_ALWAYS with existing file
748 check_create_disposition(file_path
, OPEN_ALWAYS
));
749 EXPECT_EQ(GetLastError(), ERROR_ALREADY_EXISTS
);
751 ASSERT_FALSE(fs::exists(non_existant_file
));
752 // OPEN_ALWAYS with non-existant file
754 check_create_disposition(non_existant_file
, OPEN_ALWAYS
));
755 EXPECT_EQ(GetLastError(), 0);
757 ASSERT_TRUE(fs::remove(non_existant_file
));
759 // TRUNCATE_EXISTING with existing file
761 check_create_disposition(file_path
, TRUNCATE_EXISTING
));
763 // TRUNCATE_EXISTING with non-existant file
765 check_create_disposition(non_existant_file
,
767 EXPECT_EQ(GetLastError(), ERROR_FILE_NOT_FOUND
);
770 ASSERT_TRUE(fs::remove(file_path
));