[PATCH v7 2/4] robocopy/tests: Add basic conformance tests

Florian Eder others.meder at gmail.com
Sat Oct 16 06:13:41 CDT 2021


Basic conformance tests create test source directories with
files and sub folders, 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 <others.meder at gmail.com>
---
v7: Added conformance tests for the behaiviour added in the following patches,
merged patch 2 and 6 from v6
---
 configure                           |   1 +
 configure.ac                        |   1 +
 programs/robocopy/tests/Makefile.in |   4 +
 programs/robocopy/tests/robocopy.c  | 411 ++++++++++++++++++++++++++++
 4 files changed, 417 insertions(+)
 create mode 100644 programs/robocopy/tests/Makefile.in
 create mode 100644 programs/robocopy/tests/robocopy.c

diff --git a/configure b/configure
index fd62dd6e078..49e43ebebdb 100755
--- a/configure
+++ b/configure
@@ -20990,6 +20990,7 @@ wine_fn_config_makefile programs/regini enable_regini
 wine_fn_config_makefile programs/regsvcs enable_regsvcs
 wine_fn_config_makefile programs/regsvr32 enable_regsvr32
 wine_fn_config_makefile programs/robocopy enable_robocopy
+wine_fn_config_makefile programs/robocopy/tests enable_tests
 wine_fn_config_makefile programs/rpcss enable_rpcss
 wine_fn_config_makefile programs/rundll.exe16 enable_win16
 wine_fn_config_makefile programs/rundll32 enable_rundll32
diff --git a/configure.ac b/configure.ac
index 22d3cd10eeb..fa7aae50f66 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3873,6 +3873,7 @@ WINE_CONFIG_MAKEFILE(programs/regini)
 WINE_CONFIG_MAKEFILE(programs/regsvcs)
 WINE_CONFIG_MAKEFILE(programs/regsvr32)
 WINE_CONFIG_MAKEFILE(programs/robocopy)
+WINE_CONFIG_MAKEFILE(programs/robocopy/tests)
 WINE_CONFIG_MAKEFILE(programs/rpcss)
 WINE_CONFIG_MAKEFILE(programs/rundll.exe16,enable_win16)
 WINE_CONFIG_MAKEFILE(programs/rundll32)
diff --git a/programs/robocopy/tests/Makefile.in b/programs/robocopy/tests/Makefile.in
new file mode 100644
index 00000000000..d86e70f4c43
--- /dev/null
+++ b/programs/robocopy/tests/Makefile.in
@@ -0,0 +1,4 @@
+TESTDLL   = robocopy.exe
+
+C_SRCS = \
+	robocopy.c
diff --git a/programs/robocopy/tests/robocopy.c b/programs/robocopy/tests/robocopy.c
new file mode 100644
index 00000000000..76279f476f9
--- /dev/null
+++ b/programs/robocopy/tests/robocopy.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright 2021 Florian Eder
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <wchar.h>
+#include <wine/test.h>
+
+WCHAR temp_path[MAX_PATH];
+
+static BOOL run_cmd(const WCHAR *cmd, DWORD *exit_code)
+{
+    STARTUPINFOW startup_info;
+    PROCESS_INFORMATION process_info;
+    WCHAR cmd_copy[256];
+
+    memset(&startup_info, 0, sizeof(STARTUPINFOW));
+    startup_info.dwFlags = STARTF_USESTDHANDLES;
+    startup_info.hStdInput = INVALID_HANDLE_VALUE;
+    startup_info.hStdOutput = INVALID_HANDLE_VALUE;
+    startup_info.hStdError = INVALID_HANDLE_VALUE;
+
+    swprintf(cmd_copy, ARRAY_SIZE(cmd_copy), L"%s", cmd);
+
+    if (!CreateProcessW(NULL, cmd_copy, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info))
+        return FALSE;
+
+    if (WaitForSingleObject(process_info.hProcess, 30000) == WAIT_TIMEOUT)
+        return FALSE;
+
+    GetExitCodeProcess(process_info.hProcess, exit_code);
+
+    CloseHandle(process_info.hThread);
+    CloseHandle(process_info.hProcess);
+
+    return TRUE;
+}
+
+static void create_test_file(const WCHAR *relative_path, size_t size)
+{
+    HANDLE handle;
+    WCHAR path[MAX_PATH];
+    swprintf(path, ARRAY_SIZE(path), L"%s%s", temp_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", debugstr_w(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", debugstr_w(path), GetLastError());
+    }
+    CloseHandle(handle);
+}
+
+static void create_test_folder(const WCHAR *relative_path)
+{
+    WCHAR path[MAX_PATH];
+    swprintf(path, ARRAY_SIZE(path), L"%s%s", temp_path, relative_path);
+
+    CreateDirectoryW(path, NULL);
+}
+
+static void create_test_source_folder(void)
+{
+    create_test_folder(L"source");
+    create_test_folder(L"source\\folderA");
+    create_test_folder(L"source\\folderB");
+    create_test_folder(L"source\\folderC");
+    create_test_folder(L"source\\folderA\\folderD");
+    create_test_folder(L"source\\folderA\\folderE");
+    create_test_file(L"source\\fileA.a", 4000);
+    create_test_file(L"source\\fileB.b", 8000);
+    create_test_file(L"source\\folderA\\fileC.c", 60);
+    create_test_file(L"source\\folderA\\fileD.d", 80);
+    create_test_file(L"source\\folderA\\folderD\\fileE.e", 10000);
+    create_test_file(L"source\\folderB\\fileF.f", 10000);
+    create_test_file(L"source\\folderB\\fileG.g", 200);
+}
+
+static void check_file(const WCHAR *relative_path, BOOL should_exist)
+{
+    WCHAR path[MAX_PATH];
+    swprintf(path, ARRAY_SIZE(path), L"%s%s", temp_path, relative_path);
+
+    ok (DeleteFileW(path) == should_exist, "file %s expected exist to be %d, but is %d\n", debugstr_w(relative_path), should_exist, !should_exist);
+    if (!should_exist)
+        ok(GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_PATH_NOT_FOUND,
+           "file %s DeleteFileW returned error %d, should not exist\n",
+           debugstr_w(relative_path),
+           GetLastError());
+}
+
+static void check_folder(const WCHAR *relative_path, BOOL should_exist)
+{
+    WCHAR path[MAX_PATH];
+    swprintf(path, ARRAY_SIZE(path), L"%s%s", temp_path, relative_path);
+
+    ok (RemoveDirectoryW(path) == should_exist, "folder %s expected exist to be %d, but is %d\n", debugstr_w(relative_path), should_exist, !should_exist);
+    if (!should_exist)
+        ok(GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_PATH_NOT_FOUND,
+           "folder %s RemoveDirectoryW returned error %d, should not exist\n",
+           debugstr_w(relative_path),
+           GetLastError());
+}
+
+static void check_basic_copy_1_test(void)
+{
+    check_file(L"source\\fileA.a", TRUE);
+    check_file(L"source\\fileB.b", TRUE);
+    check_file(L"source\\folderA\\fileC.c", TRUE);
+    check_file(L"source\\folderA\\fileD.d", TRUE);
+    check_file(L"source\\folderA\\folderD\\fileE.e", TRUE);
+    check_file(L"source\\folderB\\fileF.f", TRUE);
+    check_file(L"source\\folderB\\fileG.g", TRUE);
+    check_folder(L"source\\folderA\\folderD", TRUE);
+    check_folder(L"source\\folderA\\folderE", TRUE);
+    check_folder(L"source\\folderA", TRUE);
+    check_folder(L"source\\folderB", TRUE);
+    check_folder(L"source\\folderC", TRUE);
+    check_folder(L"source", TRUE);
+
+    todo_wine check_file(L"destination\\fileA.a", TRUE);
+    todo_wine check_file(L"destination\\fileB.b", TRUE);
+    check_file(L"destination\\folderA\\fileC.c", FALSE);
+    check_file(L"destination\\folderA\\fileD.d", FALSE);
+    check_file(L"destination\\folderA\\folderD\\fileE.e", FALSE);
+    check_file(L"destination\\folderB\\fileF.f", FALSE);
+    check_file(L"destination\\folderB\\fileG.g", FALSE);
+    check_folder(L"destination\\folderA\\folderD", FALSE);
+    check_folder(L"destination\\folderA\\folderE", FALSE);
+    check_folder(L"destination\\folderA", FALSE);
+    check_folder(L"destination\\folderB", FALSE);
+    check_folder(L"destination\\folderC", FALSE);
+    todo_wine check_folder(L"destination", TRUE);
+}
+
+static void check_basic_copy_2_test(void)
+{
+    check_file(L"source\\fileA.a", TRUE);
+    check_file(L"source\\fileB.b", TRUE);
+    check_file(L"source\\folderA\\fileC.c", TRUE);
+    check_file(L"source\\folderA\\fileD.d", TRUE);
+    check_file(L"source\\folderA\\folderD\\fileE.e", TRUE);
+    check_file(L"source\\folderB\\fileF.f", TRUE);
+    check_file(L"source\\folderB\\fileG.g", TRUE);
+    check_folder(L"source\\folderA\\folderD", TRUE);
+    check_folder(L"source\\folderA\\folderE", TRUE);
+    check_folder(L"source\\folderA", TRUE);
+    check_folder(L"source\\folderB", TRUE);
+    check_folder(L"source\\folderC", TRUE);
+    check_folder(L"source", TRUE);
+
+    check_file(L"destination\\fileA.a", TRUE);
+    todo_wine check_file(L"destination\\fileB.b", TRUE);
+    check_file(L"destination\\folderA\\fileC.c", FALSE);
+    check_file(L"destination\\folderA\\fileD.d", FALSE);
+    check_file(L"destination\\folderA\\folderD\\fileE.e", FALSE);
+    check_file(L"destination\\folderB\\fileF.f", FALSE);
+    check_file(L"destination\\folderB\\fileG.g", FALSE);
+    check_folder(L"destination\\folderA\\folderD", FALSE);
+    check_folder(L"destination\\folderA\\folderE", FALSE);
+    check_folder(L"destination\\folderA", FALSE);
+    check_folder(L"destination\\folderB", FALSE);
+    check_folder(L"destination\\folderC", FALSE);
+    check_folder(L"destination", TRUE);
+}
+
+static void check_no_copy_test(void)
+{
+    check_file(L"source\\fileA.a", TRUE);
+    check_file(L"source\\fileB.b", TRUE);
+    check_file(L"source\\folderA\\fileC.c", TRUE);
+    check_file(L"source\\folderA\\fileD.d", TRUE);
+    check_file(L"source\\folderA\\folderD\\fileE.e", TRUE);
+    check_file(L"source\\folderB\\fileF.f", TRUE);
+    check_file(L"source\\folderB\\fileG.g", TRUE);
+    check_folder(L"source\\folderA\\folderD", TRUE);
+    check_folder(L"source\\folderA\\folderE", TRUE);
+    check_folder(L"source\\folderA", TRUE);
+    check_folder(L"source\\folderB", TRUE);
+    check_folder(L"source\\folderC", TRUE);
+    check_folder(L"source", TRUE);
+
+    check_file(L"destination\\fileA.a", FALSE);
+    check_file(L"destination\\fileB.b", FALSE);
+    check_file(L"destination\\folderA\\fileC.c", FALSE);
+    check_file(L"destination\\folderA\\fileD.d", FALSE);
+    check_file(L"destination\\folderA\\folderD\\fileE.e", FALSE);
+    check_file(L"destination\\folderB\\fileF.f", FALSE);
+    check_file(L"destination\\folderB\\fileG.g", FALSE);
+    check_folder(L"destination\\folderA\\folderD", FALSE);
+    check_folder(L"destination\\folderA\\folderE", FALSE);
+    check_folder(L"destination\\folderA", FALSE);
+    check_folder(L"destination\\folderB", FALSE);
+    check_folder(L"destination\\folderC", FALSE);
+    check_folder(L"destination", FALSE);
+}
+
+static void check_wildcard_1_test(void)
+{
+    check_file(L"source\\fileA.a", TRUE);
+    check_file(L"source\\fileB.b", TRUE);
+    check_file(L"source\\folderA\\fileC.c", TRUE);
+    check_file(L"source\\folderA\\fileD.d", TRUE);
+    check_file(L"source\\folderA\\folderD\\fileE.e", TRUE);
+    check_file(L"source\\folderB\\fileF.f", TRUE);
+    check_file(L"source\\folderB\\fileG.g", TRUE);
+    check_folder(L"source\\folderA\\folderD", TRUE);
+    check_folder(L"source\\folderA\\folderE", TRUE);
+    check_folder(L"source\\folderA", TRUE);
+    check_folder(L"source\\folderB", TRUE);
+    check_folder(L"source\\folderC", TRUE);
+    check_folder(L"source", TRUE);
+
+    todo_wine check_file(L"destination\\fileA.a", TRUE);
+    check_file(L"destination\\fileB.b", FALSE);
+    check_file(L"destination\\folderA\\fileC.c", FALSE);
+    check_file(L"destination\\folderA\\fileD.d", FALSE);
+    check_file(L"destination\\folderA\\folderD\\fileE.e", FALSE);
+    check_file(L"destination\\folderB\\fileF.f", FALSE);
+    check_file(L"destination\\folderB\\fileG.g", FALSE);
+    check_folder(L"destination\\folderA\\folderD", FALSE);
+    check_folder(L"destination\\folderA\\folderE", FALSE);
+    check_folder(L"destination\\folderA", FALSE);
+    check_folder(L"destination\\folderB", FALSE);
+    check_folder(L"destination\\folderC", FALSE);
+    todo_wine check_folder(L"destination", TRUE);
+}
+
+START_TEST(robocopy)
+{
+    DWORD exit_code;
+    WCHAR temp_cmd[512];
+    int i;
+    const WCHAR *invalid_syntax_tests[] =
+    {
+        L"robocopy",
+        L"robocopy invalid_folder",
+        L"robocopy -flag invalid_folder",
+        L"robocopy invalid_folder destination",
+        L"robocopy -?",
+        L"robocopy invalid_folder -?",
+        L"robocopy invalid_folder /?",
+    };
+
+    /* robocopy is only available from Vista onwards, abort test if not available */
+    if (!run_cmd(L"robocopy", &exit_code)) return;
+
+    ok(GetTempPathW(ARRAY_SIZE(temp_path), temp_path) != 0, "couldn't get temp folder path\n");
+    wcscat(temp_path, L"robocopy_test\\");
+    ok(CreateDirectoryW(temp_path, NULL), "couldn't create temp test folder %s, error %d\n", debugstr_w(temp_path), GetLastError());
+    ok(SetCurrentDirectoryW(temp_path), "couldn't set CWD to temp folder %s\n", debugstr_w(temp_path));
+
+    for (i = 0; i < ARRAY_SIZE(invalid_syntax_tests); i++)
+    {
+        run_cmd(invalid_syntax_tests[i], &exit_code);
+        ok(exit_code == 16, "unexpected exit code %d from command %s\n", exit_code, debugstr_w(invalid_syntax_tests[i]));
+    }
+
+    winetest_push_context("basic copy test 1");
+    create_test_source_folder();
+    run_cmd(L"robocopy source destination /r:1 /w:0", &exit_code);
+    todo_wine ok(exit_code == 1, "unexpected exit code %d\n", exit_code);
+    check_basic_copy_1_test();
+    winetest_pop_context();
+
+    winetest_push_context("basic copy test 2");
+    create_test_source_folder();
+    run_cmd(L"robocopy ./source third_folder/../destination /r:1 /w:0", &exit_code);
+    todo_wine ok(exit_code == 1, "unexpected exit code %d\n", exit_code);
+    check_basic_copy_1_test();
+    winetest_pop_context();
+
+    winetest_push_context("basic copy test 3");
+    create_test_source_folder();
+    create_test_folder(L"destination");
+    create_test_file(L"destination\\fileA.a", 9000);
+    run_cmd(L"robocopy ./source source/../destination /r:1 /w:0", &exit_code);
+    todo_wine ok(exit_code == 1, "unexpected exit code %d\n", exit_code);
+    check_basic_copy_2_test();
+    winetest_pop_context();
+
+    winetest_push_context("basic copy test 4");
+    create_test_source_folder();
+    swprintf(temp_cmd, ARRAY_SIZE(temp_cmd),
+                L"robocopy %s\\source %s\\destination /r:1 /w:0",
+                temp_path, temp_path);
+    run_cmd(temp_cmd, &exit_code);
+    todo_wine ok(exit_code == 1, "unexpected exit code %d\n", exit_code);
+    check_basic_copy_1_test();
+    winetest_pop_context();
+
+    winetest_push_context("basic copy test 5");
+    create_test_source_folder();
+    swprintf(temp_cmd, ARRAY_SIZE(temp_cmd),
+                L"robocopy %s\\source destination /r:1 /w:0",
+                temp_path);
+    run_cmd(temp_cmd, &exit_code);
+    todo_wine ok(exit_code == 1, "unexpected exit code %d\n", exit_code);
+    check_basic_copy_1_test();
+    winetest_pop_context();
+
+    winetest_push_context("basic copy test 6");
+    create_test_source_folder();
+    swprintf(temp_cmd, ARRAY_SIZE(temp_cmd),
+                L"robocopy source %s\\destination /r:1 /w:0",
+                temp_path);
+    run_cmd(temp_cmd, &exit_code);
+    todo_wine ok(exit_code == 1, "unexpected exit code %d\n", exit_code);
+    check_basic_copy_1_test();
+    winetest_pop_context();
+
+    winetest_push_context("basic copy test 7");
+    create_test_source_folder();
+    swprintf(temp_cmd, ARRAY_SIZE(temp_cmd),
+                L"robocopy %s\\third_folder\\..\\source %s\\third_folder\\..\\destination /r:1 /w:0",
+                temp_path, temp_path);
+    run_cmd(temp_cmd, &exit_code);
+    todo_wine ok(exit_code == 1, "unexpected exit code %d\n", exit_code);
+    check_basic_copy_1_test();
+    winetest_pop_context();
+
+    winetest_push_context("invalid copy test 1");
+    create_test_source_folder();
+    run_cmd(L"robocopy source source /r:1 /w:0", &exit_code);
+    todo_wine ok(exit_code == 0, "unexpected exit code %d\n", exit_code);
+    check_no_copy_test();
+    winetest_pop_context();
+
+    winetest_push_context("invalid copy test 2");
+    create_test_source_folder();
+    run_cmd(L"robocopy invalid_folder destination /r:1 /w:0", &exit_code);
+    ok(exit_code == 16, "unexpected exit code %d\n", exit_code);
+    check_no_copy_test();
+    winetest_pop_context();
+
+    winetest_push_context("wildcard test 1");
+    create_test_source_folder();
+    run_cmd(L"robocopy source destination file?.? /r:1 /w:0", &exit_code);
+    todo_wine ok(exit_code == 1, "unexpected exit code %d\n", exit_code);
+    check_basic_copy_1_test();
+    winetest_pop_context();
+
+    winetest_push_context("wildcard test 2");
+    create_test_source_folder();
+    run_cmd(L"robocopy source destination file* /r:1 /w:0", &exit_code);
+    todo_wine ok(exit_code == 1, "unexpected exit code %d\n", exit_code);
+    check_basic_copy_1_test();
+    winetest_pop_context();
+
+    winetest_push_context("wildcard test 3");
+    create_test_source_folder();
+    run_cmd(L"robocopy source destination *le?.? /r:1 /w:0", &exit_code);
+    todo_wine ok(exit_code == 1, "unexpected exit code %d\n", exit_code);
+    check_basic_copy_1_test();
+    winetest_pop_context();
+
+    winetest_push_context("wildcard test 4");
+    create_test_source_folder();
+    run_cmd(L"robocopy source destination *A.? /r:1 /w:0", &exit_code);
+    todo_wine ok(exit_code == 1, "unexpected exit code %d\n", exit_code);
+    check_wildcard_1_test();
+    winetest_pop_context();
+
+    winetest_push_context("wildcard test 5");
+    create_test_source_folder();
+    run_cmd(L"robocopy sour?e destination /r:1 /w:0", &exit_code);
+    ok(exit_code == 16, "unexpected exit code %d\n", exit_code);
+    check_no_copy_test();
+    winetest_pop_context();
+
+    winetest_push_context("wildcard test 6");
+    create_test_source_folder();
+    run_cmd(L"robocopy s* destination /r:1 /w:0", &exit_code);
+    ok(exit_code == 16, "unexpected exit code %d\n", exit_code);
+    check_no_copy_test();
+    winetest_pop_context();
+
+    winetest_push_context("flag parser test 1");
+    create_test_source_folder();
+    run_cmd(L"robocopy /r:1 source destination /r:1 /w:0", &exit_code);
+    todo_wine ok(exit_code == 1, "unexpected exit code %d\n", exit_code);
+    check_basic_copy_1_test();
+    winetest_pop_context();
+
+    winetest_push_context("flag parser test 2");
+    create_test_source_folder();
+    run_cmd(L"robocopy source /r:1 destination /r:1 /w:0", &exit_code);
+    todo_wine ok(exit_code == 1, "unexpected exit code %d\n", exit_code);
+    check_basic_copy_1_test();
+    winetest_pop_context();
+
+    SetCurrentDirectoryW(L"..");
+    ok(RemoveDirectoryW(temp_path), "could not remove temp path %s, error %d\n", debugstr_w(temp_path), GetLastError());
+}
-- 
2.32.0




More information about the wine-devel mailing list