[PATCH 13/13] setupapi/tests: Add tests for source media path resolution.

Zebediah Figura z.figura12 at gmail.com
Wed May 1 18:24:14 CDT 2019


Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/setupapi/tests/install.c | 728 ++++++++++++++++++++++++++++++++++
 1 file changed, 728 insertions(+)

diff --git a/dlls/setupapi/tests/install.c b/dlls/setupapi/tests/install.c
index 32002a97b9..15b789fe82 100644
--- a/dlls/setupapi/tests/install.c
+++ b/dlls/setupapi/tests/install.c
@@ -1088,6 +1088,733 @@ static void test_install_files_queue(void)
     ok(ret, "Failed to delete INF file, error %u.\n", GetLastError());
 }
 
+static unsigned int got_need_media, got_copy_error;
+static unsigned int testmode;
+
+static UINT WINAPI need_media_cb(void *context, UINT message, UINT_PTR param1, UINT_PTR param2)
+{
+    if (winetest_debug > 1) trace("%p, %#x, %#lx, %#lx\n", context, message, param1, param2);
+
+    if (message == SPFILENOTIFY_NEEDMEDIA)
+    {
+        const SOURCE_MEDIA_A *media = (const SOURCE_MEDIA_A *)param1;
+        char *path = (char *)param2;
+        UINT ret;
+
+        /* The default callback will fill out SourcePath, but as long as DOIT
+         * is returned, it's ignored. */
+        ok(!path[0], "Test %u: got path '%s'.\n", testmode, path);
+        ret = SetupDefaultQueueCallbackA(context, message, param1, param2);
+        ok(!strcmp(path, media->SourcePath), "Test %u: got path '%s'.\n", testmode, path);
+        ok(ret == FILEOP_DOIT, "Got unexpected return value %u.\n", ret);
+        path[0] = 0;
+
+        if (testmode == 0)
+            ok(media->Flags == SP_COPY_WARNIFSKIP, "Got Flags %#x.\n", media->Flags);
+        else
+            ok(!media->Flags, "Got Flags %#x for test %u.\n", media->Flags, testmode);
+
+        switch (testmode)
+        {
+        case 0:
+            ok(!media->Tagfile, "Got Tagfile '%s'.\n", media->Tagfile);
+            ok(!strcmp(media->Description, "File One"), "Got Description '%s'.\n", media->Description);
+            ok(!strcmp(media->SourcePath, "src"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "one.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            break;
+        case 1:
+            ok(!media->Tagfile, "Got Tagfile '%s'.\n", media->Tagfile);
+            ok(!media->Description, "Got Description '%s'.\n", media->Description);
+            ok(!strcmp(media->SourcePath, "src\\beta"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "two.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            break;
+        case 2:
+            ok(!strcmp(media->Tagfile, "faketag"), "Got Tagfile '%s'.\n", media->Tagfile);
+            ok(!strcmp(media->Description, "desc"), "Got Description '%s'.\n", media->Description);
+            ok(!strcmp(media->SourcePath, "src\\beta"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "two.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            break;
+        case 3:
+            ok(!media->Tagfile, "Got Tagfile '%s'.\n", media->Tagfile);
+            ok(!strcmp(media->Description, "heis"), "Got Description '%s'.\n", media->Description);
+            ok(!strcmp(media->SourcePath, "src"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "one.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            break;
+        case 4:
+            ok(!media->Tagfile, "Got Tagfile '%s'.\n", media->Tagfile);
+            ok(!strcmp(media->Description, "heis"), "Got Description '%s'.\n", media->Description);
+            ok(!strcmp(media->SourcePath, "src\\beta"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "two.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            break;
+        case 5:
+            ok(!media->Tagfile, "Got Tagfile '%s'.\n", media->Tagfile);
+            ok(!strcmp(media->Description, "duo"), "Got Description '%s'.\n", media->Description);
+            ok(!strcmp(media->SourcePath, "src\\alpha"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "three.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            break;
+        case 6:
+            ok(!strcmp(media->SourcePath, "src"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "one.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            testmode = 7;
+            break;
+        case 7:
+            ok(!strcmp(media->SourcePath, "src/alpha"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "three.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            break;
+        case 8:
+            ok(!strcmp(media->SourcePath, "src"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "one.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            testmode = 9;
+            break;
+        case 9:
+            ok(!strcmp(media->SourcePath, "src\\alpha"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "three.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            break;
+        case 10:
+            ok(!strcmp(media->Description, "desc1"), "Got description '%s'.\n", media->Description);
+            ok(!strcmp(media->SourcePath, "src"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "one.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            testmode = 11;
+            break;
+        case 11:
+            ok(!media->Description, "Got description '%s'.\n", media->Description);
+            ok(!strcmp(media->SourcePath, "src\\beta"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "two.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            break;
+        case 12:
+            ok(!strcmp(media->Description, "desc1"), "Got description '%s'.\n", media->Description);
+            ok(!strcmp(media->Tagfile, "faketag"), "Got Tagfile '%s'.\n", media->Tagfile);
+            ok(!strcmp(media->SourcePath, "src"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "one.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            testmode = 13;
+            break;
+        case 13:
+            ok(!strcmp(media->Description, "desc1"), "Got description '%s'.\n", media->Description);
+            ok(!strcmp(media->Tagfile, "faketag2"), "Got Tagfile '%s'.\n", media->Tagfile);
+            ok(!strcmp(media->SourcePath, "src\\beta"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "two.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            break;
+        case 14:
+            ok(!strcmp(media->Description, "desc"), "Got description '%s'.\n", media->Description);
+            ok(!strcmp(media->Tagfile, "treis.cab"), "Got Tagfile '%s'.\n", media->Tagfile);
+            ok(!strcmp(media->SourcePath, "src"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "four.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            break;
+        case 15:
+            ok(!strcmp(media->Description, "desc"), "Got description '%s'.\n", media->Description);
+            ok(!strcmp(media->Tagfile, "tessares.cab"), "Got Tagfile '%s'.\n", media->Tagfile);
+            ok(!strcmp(media->SourcePath, "src\\alpha"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "seven.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            break;
+        case 16:
+            ok(!media->Description, "Got description '%s'.\n", media->Description);
+            ok(!strcmp(media->SourcePath, "src/alpha"), "Got SourcePath '%s'.\n", media->SourcePath);
+            ok(!strcmp(media->SourceFile, "six.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            break;
+        }
+
+        ++got_need_media;
+
+        return ret;
+    }
+    else if (message == SPFILENOTIFY_COPYERROR)
+    {
+        const FILEPATHS_A *paths = (const FILEPATHS_A *)param1;
+        ok(0, "Got unexpected copy error %s -> %s.\n", paths->Source, paths->Target);
+    }
+
+    return SetupDefaultQueueCallbackA(context, message, param1, param2);
+}
+
+static UINT WINAPI need_media_newpath_cb(void *context, UINT message, UINT_PTR param1, UINT_PTR param2)
+{
+    if (winetest_debug > 1) trace("%p, %#x, %#lx, %#lx\n", context, message, param1, param2);
+
+    if (message == SPFILENOTIFY_NEEDMEDIA)
+    {
+        const SOURCE_MEDIA_A *media = (const SOURCE_MEDIA_A *)param1;
+        char *path = (char *)param2;
+
+        ++got_need_media;
+
+        if (testmode == 1)
+            strcpy(path, "src\\alpha");
+        else if (testmode == 2)
+        {
+            if (got_need_media == 1)
+                strcpy(path, "src\\alpha");
+            else
+            {
+                ok(!strcmp(media->SourcePath, "src\\alpha"), "Got SourcePath '%s'.\n", media->SourcePath);
+                strcpy(path, "src");
+            }
+        }
+        else if (testmode == 5)
+        {
+            if (got_need_media == 1)
+            {
+                ok(!strcmp(media->SourcePath, "fake"), "Got SourcePath '%s'.\n", media->SourcePath);
+                ok(!strcmp(media->SourceFile, "one.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+                return FILEOP_SKIP;
+            }
+            else
+            {
+                ok(!strcmp(media->SourcePath, "fake\\alpha"), "Got SourcePath '%s'.\n", media->SourcePath);
+                ok(!strcmp(media->SourceFile, "three.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+                strcpy(path, "src\\alpha");
+            }
+        }
+        else if (testmode == 6)
+        {
+            /* SourcePath is not really consistent here, but it's not supplied
+             * from the INF file. Usually it's a drive root, but not always. */
+            ok(!strcmp(media->SourceFile, "one.txt"), "Got SourceFile '%s'.\n", media->SourceFile);
+            ok(!media->Description, "Got Description '%s'.\n", media->Description);
+            ok(!media->Tagfile, "Got Tagfile '%s'.\n", media->Tagfile);
+            strcpy(path, "src");
+        }
+        else
+            strcpy(path, "src");
+
+        return FILEOP_NEWPATH;
+    }
+    else if (message == SPFILENOTIFY_COPYERROR)
+    {
+        char *path = (char *)param2;
+
+        ++got_copy_error;
+
+        if (testmode == 3)
+        {
+            strcpy(path, "src\\alpha");
+            return FILEOP_NEWPATH;
+        }
+        else if (testmode == 4)
+        {
+            if (got_copy_error == 1)
+                strcpy(path, "fake2");
+            else
+                strcpy(path, "src\\alpha");
+            return FILEOP_NEWPATH;
+        }
+        else
+            return FILEOP_SKIP;
+    }
+
+    return SetupDefaultQueueCallbackA(context, message, param1, param2);
+}
+
+#define run_queue(a,b) run_queue_(__LINE__,a,b)
+static void run_queue_(unsigned int line, HSPFILEQ queue, PSP_FILE_CALLBACK_A cb)
+{
+    void *context = SetupInitDefaultQueueCallbackEx(NULL, INVALID_HANDLE_VALUE, 0, 0, 0);
+    BOOL ret;
+    ok_(__FILE__,line)(!!context, "Failed to create callback context, error %#x.\n", GetLastError());
+    ret = SetupCommitFileQueueA(NULL, queue, cb, context);
+    ok_(__FILE__,line)(ret, "Failed to commit queue, error %#x.\n", GetLastError());
+    SetupTermDefaultQueueCallback(context);
+    ret = SetupCloseFileQueue(queue);
+    ok_(__FILE__,line)(ret, "Failed to close queue, error %#x.\n", GetLastError());
+}
+
+static void test_need_media(void)
+{
+    static const char inf_data[] = "[Version]\n"
+            "Signature=\"$Chicago$\"\n"
+            "[section1]\n"
+            "one.txt\n"
+            "[section2]\n"
+            "two.txt\n"
+            "[section3]\n"
+            "three.txt\n"
+            "[section4]\n"
+            "one.txt\n"
+            "two.txt\n"
+            "three.txt\n"
+            "[install_section]\n"
+            "CopyFiles=section1\n"
+            "[SourceDisksNames]\n"
+            "1=heis\n"
+            "2=duo,,,alpha\n"
+            "[SourceDisksFiles]\n"
+            "one.txt=1\n"
+            "two.txt=1,beta\n"
+            "three.txt=2\n"
+            "[DestinationDirs]\n"
+            "DefaultDestDir=40000,dst\n";
+
+    SP_FILE_COPY_PARAMS_A copy_params = {sizeof(copy_params)};
+    char path[MAX_PATH];
+    HSPFILEQ queue;
+    HINF hinf;
+    BOOL ret;
+
+    create_inf_file(inffile, inf_data);
+
+    sprintf(path, "%s\\%s", CURR_DIR, inffile);
+    hinf = SetupOpenInfFileA(path, NULL, INF_STYLE_WIN4, NULL);
+    ok(hinf != INVALID_HANDLE_VALUE, "Failed to open INF file, error %#x.\n", GetLastError());
+
+    ret = CreateDirectoryA("src", NULL);
+    ok(ret, "Failed to create test directory, error %u.\n", GetLastError());
+    ret = CreateDirectoryA("src/alpha", NULL);
+    ok(ret, "Failed to create test directory, error %u.\n", GetLastError());
+    ret = CreateDirectoryA("src/beta", NULL);
+    ok(ret, "Failed to create test directory, error %u.\n", GetLastError());
+    ret = CreateDirectoryA("dst", NULL);
+    ok(ret, "Failed to create test directory, error %u.\n", GetLastError());
+    create_file("src/one.txt");
+    create_file("src/beta/two.txt");
+    create_file("src/alpha/three.txt");
+    create_file("src/alpha/six.txt");
+    create_cab_file("src/treis.cab", "four.txt\0five.txt\0");
+    create_cab_file("src/alpha/tessares.cab", "seven.txt\0eight.txt\0");
+
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", NULL, "one.txt", "File One", NULL, "dst", NULL, SP_COPY_WARNIFSKIP);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+
+    /* Test with a subdirectory. */
+
+    got_need_media = 0;
+    testmode = 1;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", "beta", "two.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/two.txt"), "Destination file should exist.\n");
+
+    /* Test with a tag file. */
+
+    got_need_media = 0;
+    testmode = 2;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", "beta", "two.txt", "desc", "faketag", "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/two.txt"), "Destination file should exist.\n");
+
+    /* Test from INF file. */
+
+    got_need_media = 0;
+    testmode = 3;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopySectionA(queue, "src", hinf, NULL, "section1", 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+
+    got_need_media = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupInstallFilesFromInfSectionA(hinf, NULL, queue, "install_section", "src", 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+
+    got_need_media = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+
+    ret = SetupQueueDefaultCopyA(queue, hinf, "src", NULL, "one.txt", 0);
+    ok(!ret, "Expected failure.\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "Got error %#x.\n", GetLastError());
+
+    ret = SetupQueueDefaultCopyA(queue, hinf, "src", "one.txt", "one.txt", 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+
+    got_need_media = 0;
+    testmode = 4;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopySectionA(queue, "src", hinf, NULL, "section2", 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/two.txt"), "Destination file should exist.\n");
+
+    got_need_media = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueDefaultCopyA(queue, hinf, "src", "two.txt", "two.txt", 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/two.txt"), "Destination file should exist.\n");
+
+    got_need_media = 0;
+    testmode = 5;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopySectionA(queue, "src", hinf, NULL, "section3", 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/three.txt"), "Destination file should exist.\n");
+
+    got_need_media = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueDefaultCopyA(queue, hinf, "src", "three.txt", "three.txt", 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/three.txt"), "Destination file should exist.\n");
+
+    /* One callback is sent per source directory. */
+
+    got_need_media = 0;
+    testmode = 6;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", NULL, "one.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", "beta", "two.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src/alpha", NULL, "three.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 2, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/two.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/three.txt"), "Destination file should exist.\n");
+
+    /* The same rules apply to INF files. Here the subdir specified in the
+     * SourceDisksNames counts as part of the root directory, but the subdir in
+     * SourceDisksFiles does not. */
+
+    got_need_media = 0;
+    testmode = 8;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopySectionA(queue, "src", hinf, NULL, "section4", 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());\
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 2, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/two.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/three.txt"), "Destination file should exist.\n");
+
+    /* Descriptions and tag files also distinguish source paths. */
+
+    got_need_media = 0;
+    testmode = 10;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", NULL, "one.txt", "desc1", NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", "beta", "two.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 2, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/two.txt"), "Destination file should exist.\n");
+
+    got_need_media = 0;
+    testmode = 12;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", NULL, "one.txt", "desc1", "faketag", "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", "beta", "two.txt", "desc1", "faketag2", "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 2, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/two.txt"), "Destination file should exist.\n");
+
+    /* Test from cabinets. Subdir is only relevant for the first argument. */
+
+    got_need_media = 0;
+    testmode = 14;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", NULL, "four.txt", "desc", "treis.cab", "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", "alpha", "five.txt", "desc", "treis.cab", "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/four.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/five.txt"), "Destination file should exist.\n");
+
+    got_need_media = 0;
+    testmode = 15;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", "alpha", "seven.txt", "desc", "tessares.cab", "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", NULL, "eight.txt", "desc", "tessares.cab", "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/seven.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/eight.txt"), "Destination file should exist.\n");
+
+    got_need_media = 0;
+    testmode = 16;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueDefaultCopyA(queue, hinf, "src/alpha", "six.txt", "six.txt", 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/six.txt"), "Destination file should exist.\n");
+
+    /* Test absolute paths. */
+
+    got_need_media = 0;
+    testmode = 1;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", "beta", "two.txt", NULL, NULL, "dst", NULL, SP_COPY_SOURCE_ABSOLUTE);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/two.txt"), "Destination file should exist.\n");
+
+    got_need_media = 0;
+    testmode = 1;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "src", "beta", "two.txt", NULL, NULL, "dst", NULL, SP_COPY_SOURCEPATH_ABSOLUTE);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/two.txt"), "Destination file should exist.\n");
+
+    got_need_media = 0;
+    testmode = 5;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopySectionA(queue, "src", hinf, NULL, "section3", SP_COPY_SOURCE_ABSOLUTE);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/three.txt"), "Destination file should exist.\n");
+
+    got_need_media = 0;
+    testmode = 5;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopySectionA(queue, "src", hinf, NULL, "section3", SP_COPY_SOURCEPATH_ABSOLUTE);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(delete_file("dst/three.txt"), "Destination file should exist.\n");
+
+    /* Test returning a new path from the NEEDMEDIA callback. */
+
+    testmode = 0;
+    got_need_media = got_copy_error = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", NULL, "one.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", "alpha", "three.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_newpath_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(!got_copy_error, "Got %u copy errors.\n", got_copy_error);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/three.txt"), "Destination file should exist.\n");
+
+    /* setupapi expects the callback to return the path including the subdir
+     * for the first file. It then strips off the final element. If the final
+     * element doesn't match the given subdir exactly, then it's not stripped.
+     * To make matters even stranger, the first file copied effectively has its
+     * subdir removed. */
+
+    testmode = 1;
+    got_need_media = got_copy_error = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", "alpha", "three.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", "beta", "two.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_newpath_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(!got_copy_error, "Got %u copy errors.\n", got_copy_error);
+    ok(delete_file("dst/three.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/two.txt"), "Destination file should exist.\n");
+
+    got_need_media = got_copy_error = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", "alpha\\", "three.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", NULL, "six.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_newpath_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(!got_copy_error, "Got %u copy errors.\n", got_copy_error);
+    ok(delete_file("dst/three.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/six.txt"), "Destination file should exist.\n");
+
+    /* If the source file does not exist (even if the path is valid),
+     * SPFILENOTIFY_NEEDMEDIA is resent until it does. */
+
+    testmode = 2;
+    got_need_media = got_copy_error = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", NULL, "one.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", "alpha", "three.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_newpath_cb);
+    ok(got_need_media == 2, "Got %u callbacks.\n", got_need_media);
+    ok(!got_copy_error, "Got %u copy errors.\n", got_copy_error);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/three.txt"), "Destination file should exist.\n");
+
+    /* If a following file doesn't exist, it results in a copy error instead. */
+
+    testmode = 0;
+    got_need_media = got_copy_error = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", NULL, "one.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", NULL, "fake.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_newpath_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(got_copy_error == 1, "Got %u copy errors.\n", got_copy_error);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+
+    /* Test providing a new path from SPFILENOTIFY_COPYERROR. */
+
+    testmode = 3;
+    got_need_media = got_copy_error = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", NULL, "one.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", NULL, "three.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", NULL, "six.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_newpath_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(got_copy_error == 1, "Got %u copy errors.\n", got_copy_error);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/three.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/six.txt"), "Destination file should exist.\n");
+
+    /* SPFILENOTIFY_COPYERROR will also be resent until the copy is successful. */
+
+    testmode = 4;
+    got_need_media = got_copy_error = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", NULL, "one.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", NULL, "three.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", NULL, "six.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_newpath_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(got_copy_error == 2, "Got %u copy errors.\n", got_copy_error);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/three.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/six.txt"), "Destination file should exist.\n");
+
+    /* Test with cabinet. As above, subdir only matters for the first file. */
+
+    testmode = 0;
+    got_need_media = got_copy_error = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", NULL, "four.txt", "desc", "treis.cab", "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", "alpha", "five.txt", "desc", "treis.cab", "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_newpath_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(!got_copy_error, "Got %u copy errors.\n", got_copy_error);
+    ok(delete_file("dst/four.txt"), "Destination file should exist.\n");
+    ok(delete_file("dst/five.txt"), "Destination file should exist.\n");
+
+    /* Test returning FILEOP_SKIP from the NEEDMEDIA handler. */
+
+    testmode = 5;
+    got_need_media = got_copy_error = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", NULL, "one.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    ret = SetupQueueCopyA(queue, "fake", "alpha", "three.txt", NULL, NULL, "dst", NULL, 0);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_newpath_cb);
+    ok(got_need_media == 2, "Got %u callbacks.\n", got_need_media);
+    ok(!got_copy_error, "Got %u copy errors.\n", got_copy_error);
+    ok(!file_exists("dst/one.txt"), "Destination file should not exist.\n");
+    ok(delete_file("dst/three.txt"), "Destination file should exist.\n");
+
+    testmode = 6;
+    got_need_media = got_copy_error = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    copy_params.QueueHandle = queue;
+    copy_params.SourceFilename = "one.txt";
+    /* Leaving TargetDirectory NULL causes it to be filled with garbage on
+     * Windows, so the copy may succeed or fail. In any case it's not supplied
+     * from LayoutInf. */
+    copy_params.TargetDirectory = "dst";
+    ret = SetupQueueCopyIndirectA(&copy_params);
+    ok(ret, "Failed to queue copy, error %#x.\n", GetLastError());
+    run_queue(queue, need_media_newpath_cb);
+    ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+    ok(!got_copy_error, "Got %u copy errors.\n", got_copy_error);
+    ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+
+    got_need_media = got_copy_error = 0;
+    queue = SetupOpenFileQueue();
+    ok(queue != INVALID_HANDLE_VALUE, "Failed to open queue, error %#x.\n", GetLastError());
+    copy_params.LayoutInf = hinf;
+    /* In fact this fails with ERROR_INVALID_PARAMETER on 8+. */
+    if (SetupQueueCopyIndirectA(&copy_params))
+    {
+        run_queue(queue, need_media_newpath_cb);
+        ok(got_need_media == 1, "Got %u callbacks.\n", got_need_media);
+        ok(!got_copy_error, "Got %u copy errors.\n", got_copy_error);
+        ok(delete_file("dst/one.txt"), "Destination file should exist.\n");
+    }
+    else
+        SetupCloseFileQueue(queue);
+
+    SetupCloseInfFile(hinf);
+    delete_file("src/one.txt");
+    delete_file("src/beta/two.txt");
+    delete_file("src/beta/");
+    delete_file("src/alpha/six.txt");
+    delete_file("src/alpha/three.txt");
+    delete_file("src/alpha/tessares.cab");
+    delete_file("src/alpha/");
+    delete_file("src/treis.cab");
+    delete_file("src/");
+    delete_file("dst/");
+    ret = DeleteFileA(inffile);
+    ok(ret, "Failed to delete INF file, error %u.\n", GetLastError());
+}
+
 START_TEST(install)
 {
     char temp_path[MAX_PATH], prev_path[MAX_PATH];
@@ -1113,6 +1840,7 @@ START_TEST(install)
     test_driver_install();
     test_dirid();
     test_install_files_queue();
+    test_need_media();
 
     UnhookWindowsHookEx(hhook);
 
-- 
2.21.0




More information about the wine-devel mailing list