From: Florian Eder Subject: [PATCH v4 6/6] robocopy/tests: Add basic conformance tests Message-Id: <20210918074718.35085-6-others.meder@gmail.com> Date: Sat, 18 Sep 2021 07:47:18 +0000 In-Reply-To: <20210918074718.35085-1-others.meder@gmail.com> References: <20210918074718.35085-1-others.meder@gmail.com> Basic conformance tests create a test source directory with files and execute robocopy on it, checking whether the resulting destination directory, the remaining source directory and the exit code is as expected Signed-off-by: Florian Eder --- programs/robocopy/tests/robocopy.c | 188 ++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 1 deletion(-) diff --git a/programs/robocopy/tests/robocopy.c b/programs/robocopy/tests/robocopy.c index 2093a990f03..fdeddb04907 100644 --- a/programs/robocopy/tests/robocopy.c +++ b/programs/robocopy/tests/robocopy.c @@ -48,9 +48,153 @@ static DWORD execute_robocopy(const WCHAR *cmd_line) return exit_code; } +static void create_test_file(const WCHAR *relative_path, size_t size, LONGLONG fixed_filetime, long filetime_offset) +{ + HANDLE handle; + WCHAR path[1024]; + wcscpy(path, L"\\\\?\\"); + GetTempPathW(ARRAY_SIZE(path) - 4, &(path[4])); + wcscat(path, relative_path); + handle = CreateFileW(path, FILE_GENERIC_WRITE | FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL); + ok(handle != INVALID_HANDLE_VALUE, "creation of %S failed (0x%08x)\n", path, GetLastError()); + if (size != 0) + { + BYTE *data; + DWORD bytes_written; + data = calloc(size, sizeof(BYTE)); + ok(WriteFile(handle, data, size, &bytes_written, NULL), "writing to %S failed (%d)\n", path, GetLastError()); + } + if (fixed_filetime != 0) + { + FILETIME time; + LARGE_INTEGER time_as_integer; + time_as_integer.QuadPart = fixed_filetime; + time.dwHighDateTime = time_as_integer.HighPart; + time.dwLowDateTime = time_as_integer.LowPart; + ok(SetFileTime(handle, &time, &time, &time), "filetime manipulation of %S failed (%d)\n", path, GetLastError()); + } + if (filetime_offset != 0) + { + FILETIME time, modified_time, access_time; + LARGE_INTEGER time_as_integer; + GetFileTime(handle, &time, &modified_time, &access_time); + /* FILETIME is no union with LONGLONG / LONG64, casting could be unsafe */ + time_as_integer.HighPart = time.dwHighDateTime; + time_as_integer.LowPart = time.dwLowDateTime; + /* 1000 * 1000 * 60 * 60 * 24 = 86400000000ns per day */ + time_as_integer.QuadPart += 864000000000LL * filetime_offset; + time.dwHighDateTime = time_as_integer.HighPart; + time.dwLowDateTime = time_as_integer.LowPart; + ok(SetFileTime(handle, &time, &time, &time), "filetime manipulation of %S failed (%d)\n", path, GetLastError()); + } + CloseHandle(handle); +} + +static void create_test_folder(const WCHAR *relative_path) +{ + WCHAR path[1024]; + wcscpy(path, L"\\\\?\\"); + GetTempPathW(ARRAY_SIZE(path) - 4, &(path[4])); + wcscat(path, relative_path); + CreateDirectoryW(path, NULL); +} + +static void create_test_source_folder(void) +{ + create_test_folder(L"robocopy_source"); + create_test_folder(L"robocopy_source\\folderA"); + create_test_folder(L"robocopy_source\\folderB"); + create_test_folder(L"robocopy_source\\folderC"); + create_test_folder(L"robocopy_source\\folderA\\folderD"); + create_test_folder(L"robocopy_source\\folderA\\folderE"); + create_test_file(L"robocopy_source\\fileA.a", 4000, 0, -10); + create_test_file(L"robocopy_source\\fileB.b", 8000, 0, -2); + create_test_file(L"robocopy_source\\folderA\\fileC.c", 60, 0, -2); + create_test_file(L"robocopy_source\\folderA\\fileD.d", 80, 0, 0); + create_test_file(L"robocopy_source\\folderA\\folderD\\fileE.e", 10000, 0, -10); + create_test_file(L"robocopy_source\\folderB\\fileF.f", 10000, 132223104000000000, 0); + create_test_file(L"robocopy_source\\folderB\\fileG.g", 200, 132223104000000000, 0); +} + +static void check_file_and_delete(const WCHAR *relative_path, BOOL should_exist) +{ + WCHAR path[1024]; + wcscpy(path, L"\\\\?\\"); + GetTempPathW(ARRAY_SIZE(path) - 4, &(path[4])); + wcscat(path, relative_path); + if (!DeleteFileW(path)) + { + if (GetLastError() == ERROR_FILE_NOT_FOUND) + ok(!should_exist, "file \"%S\" does not exist, but should exist\n", relative_path); + else if (GetLastError() == ERROR_PATH_NOT_FOUND) + ok(!should_exist, "file \"%S\" and the parent directory do not exist, but should exist\n", relative_path); + else + ok(FALSE, "file \"%S\" DeleteFileW returned error %d\n", relative_path, GetLastError()); + } + else + { + ok(should_exist, "file \"%S\" should not exist, but does exist\n", relative_path); + } +} + +static void check_folder_and_delete(const WCHAR *relative_path, BOOL should_exist) +{ + WCHAR path[1024]; + wcscpy(path, L"\\\\?\\"); + GetTempPathW(ARRAY_SIZE(path) - 4, &(path[4])); + wcscat(path, relative_path); + if (!RemoveDirectoryW(path)) + { + if (GetLastError() == ERROR_FILE_NOT_FOUND) + ok(!should_exist, "directory \"%S\" does not exist, but should exist\n", relative_path); + else if (GetLastError() == ERROR_PATH_NOT_FOUND) + ok(!should_exist, "directory \"%S\" and the parent directory do not exist, but should exist\n", relative_path); + else if (GetLastError() == ERROR_DIR_NOT_EMPTY) + ok(FALSE, "directory \"%S\" is unexpectedly not empty\n", relative_path); + else + ok(FALSE, "directory \"%S\" DeleteFileW returned error %d\n", relative_path, GetLastError()); + } + else + { + ok(should_exist, "directory \"%S\" should not exist, but does exist\n", relative_path); + } +} + +static void check_basic_copy_test(void) +{ + check_file_and_delete(L"robocopy_source\\fileA.a", TRUE); + check_file_and_delete(L"robocopy_source\\fileB.b", TRUE); + check_file_and_delete(L"robocopy_source\\folderA\\fileC.c", TRUE); + check_file_and_delete(L"robocopy_source\\folderA\\fileD.d", TRUE); + check_file_and_delete(L"robocopy_source\\folderA\\folderD\\fileE.e", TRUE); + check_file_and_delete(L"robocopy_source\\folderB\\fileF.f", TRUE); + check_file_and_delete(L"robocopy_source\\folderB\\fileG.g", TRUE); + check_folder_and_delete(L"robocopy_source\\folderA\\folderD", TRUE); + check_folder_and_delete(L"robocopy_source\\folderA\\folderE", TRUE); + check_folder_and_delete(L"robocopy_source\\folderA", TRUE); + check_folder_and_delete(L"robocopy_source\\folderB", TRUE); + check_folder_and_delete(L"robocopy_source\\folderC", TRUE); + check_folder_and_delete(L"robocopy_source", TRUE); + + check_file_and_delete(L"robocopy_destination\\fileA.a", TRUE); + check_file_and_delete(L"robocopy_destination\\fileB.b", TRUE); + todo_wine check_file_and_delete(L"robocopy_destination\\folderA\\fileC.c", FALSE); + todo_wine check_file_and_delete(L"robocopy_destination\\folderA\\fileD.d", FALSE); + todo_wine check_file_and_delete(L"robocopy_destination\\folderA\\folderD\\fileE.e", FALSE); + todo_wine check_file_and_delete(L"robocopy_destination\\folderB\\fileF.f", FALSE); + todo_wine check_file_and_delete(L"robocopy_destination\\folderB\\fileG.g", FALSE); + todo_wine check_folder_and_delete(L"robocopy_destination\\folderA\\folderD", FALSE); + check_folder_and_delete(L"robocopy_destination\\folderA\\folderE", FALSE); + todo_wine check_folder_and_delete(L"robocopy_destination\\folderA", FALSE); + todo_wine check_folder_and_delete(L"robocopy_destination\\folderB", FALSE); + check_folder_and_delete(L"robocopy_destination\\folderC", FALSE); + check_folder_and_delete(L"robocopy_destination", TRUE); +} + START_TEST(robocopy) { - WCHAR temp_path[4096]; + WCHAR temp_command_line[2048], temp_path[4096]; + DWORD exit_code; ok(GetTempPathW(ARRAY_SIZE(temp_path), temp_path) != 0, "couldn't get temp folder path"); @@ -60,4 +204,46 @@ START_TEST(robocopy) ok(SetCurrentDirectoryW(temp_path), "couldn't set CWD to temp folder \"%S\"", temp_path); /* TODO: conformance tests here */ + winetest_push_context("basic copy test 1"); + create_test_source_folder(); + exit_code = execute_robocopy(L"robocopy.exe robocopy_source robocopy_destination /r:1 /w:0"); + ok(exit_code == 1, "unexpected exit code %d\n", exit_code); + check_basic_copy_test(); + winetest_pop_context(); + + winetest_push_context("basic copy test 2"); + create_test_source_folder(); + exit_code = execute_robocopy(L"robocopy.exe ./robocopy_source third_folder/../robocopy_destination /r:1 /w:0"); + ok(exit_code == 1, "unexpected exit code %d\n", exit_code); + check_basic_copy_test(); + winetest_pop_context(); + + winetest_push_context("basic copy test 3"); + create_test_source_folder(); + create_test_folder(L"robocopy_destination"); + create_test_file(L"robocopy_destination\\fileA.a", 9000, 0, -10); + exit_code = execute_robocopy(L"robocopy.exe ./robocopy_source robocopy_source/../robocopy_destination /r:1 /w:0"); + ok(exit_code == 1, "unexpected exit code %d\n", exit_code); + check_basic_copy_test(); + winetest_pop_context(); + + winetest_push_context("basic copy test 4"); + create_test_source_folder(); + swprintf(temp_command_line, ARRAY_SIZE(temp_command_line), + L"robocopy.exe %s\\robocopy_source %s\\robocopy_destination /r:1 /w:0", + temp_path, temp_path); + exit_code = execute_robocopy(temp_command_line); + ok(exit_code == 1, "unexpected exit code %d\n", exit_code); + check_basic_copy_test(); + winetest_pop_context(); + + winetest_push_context("basic copy test 5"); + create_test_source_folder(); + swprintf(temp_command_line, ARRAY_SIZE(temp_command_line), + L"robocopy.exe %s\\third_folder\\..\\robocopy_source %s\\third_folder\\..\\robocopy_destination /r:1 /w:0", + temp_path, temp_path); + exit_code = execute_robocopy(temp_command_line); + ok(exit_code == 1, "unexpected exit code %d\n", exit_code); + check_basic_copy_test(); + winetest_pop_context(); } -- 2.32.0