Signed-off-by: Zebediah Figura z.figura12@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(©_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(©_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);