From: Jiajin Cui <cuijiajin@uniontech.com> Add a test verifying that URLDownloadToCacheFileW does not leak the temporary cache file when the download is aborted. Cache entries are distributed across randomly chosen sub-directories, so the test counts matching files across all cache sub-directories before and after the aborted download instead of relying on a predicted path. Signed-off-by: Jiajin Cui <cuijiajin@uniontech.com> --- dlls/urlmon/tests/url.c | 114 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/dlls/urlmon/tests/url.c b/dlls/urlmon/tests/url.c index 16742d20ff7..66c1725cdeb 100644 --- a/dlls/urlmon/tests/url.c +++ b/dlls/urlmon/tests/url.c @@ -3746,6 +3746,117 @@ static void test_URLDownloadToFile_abort(void) DeleteFileA(dwl_htmlA); } +static DWORD count_cache_files(const WCHAR *cache_root, const WCHAR *prefix) +{ + WCHAR pattern[MAX_PATH], sub_pattern[MAX_PATH]; + WIN32_FIND_DATAW dir_data, data; + HANDLE dir_find, find; + DWORD count = 0; + + /* Cache entries are randomly distributed across sub-directories, so scan + * every sub-directory of the cache root for files matching the prefix. */ + lstrcpyW(pattern, cache_root); + lstrcatW(pattern, L"\\*"); + + dir_find = FindFirstFileW(pattern, &dir_data); + if(dir_find == INVALID_HANDLE_VALUE) + return 0; + + do { + if(!(dir_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + continue; + if(!lstrcmpW(dir_data.cFileName, L".") || !lstrcmpW(dir_data.cFileName, L"..")) + continue; + + lstrcpyW(sub_pattern, cache_root); + lstrcatW(sub_pattern, L"\\"); + lstrcatW(sub_pattern, dir_data.cFileName); + lstrcatW(sub_pattern, L"\\"); + lstrcatW(sub_pattern, prefix); + lstrcatW(sub_pattern, L"*"); + + find = FindFirstFileW(sub_pattern, &data); + if(find != INVALID_HANDLE_VALUE) { + do { + count++; + } while(FindNextFileW(find, &data)); + FindClose(find); + } + } while(FindNextFileW(dir_find, &dir_data)); + + FindClose(dir_find); + return count; +} + +static void test_URLDownloadToCacheFile_abort(void) +{ + WCHAR cache_file[MAX_PATH], cache_root[MAX_PATH], file_name[MAX_PATH]; + WCHAR url[INTERNET_MAX_URL_LENGTH]; + DWORD before, after; + HRESULT hres; + BOOL res; + + wsprintfW(url, L"http://test.winehq.org/tests/urlmon-cache-cleanup-%lu.txt", GetCurrentProcessId()); + + /* CreateUrlCacheEntryW places the file in a randomly chosen cache + * sub-directory, so the path used internally by URLDownloadToCacheFileW + * cannot be predicted. Remove the placeholder created here and compare the + * number of matching files across all cache sub-directories before and + * after the aborted download to detect a leaked temporary file. */ + res = CreateUrlCacheEntryW(url, 0, PathFindExtensionW(url), cache_file, 0); + ok(res, "CreateUrlCacheEntryW failed: %lu\n", GetLastError()); + if (!res) + return; + + lstrcpyW(cache_root, cache_file); + PathRemoveFileSpecW(cache_root); /* strip file name -> sub-directory */ + PathRemoveFileSpecW(cache_root); /* strip sub-directory -> cache root */ + + res = DeleteFileW(cache_file); + ok(res, "DeleteFileW failed: %lu\n", GetLastError()); + + before = count_cache_files(cache_root, L"urlmon-cache-cleanup-"); + + init_bind_test(HTTP_TEST, BINDTEST_FILEDWLAPI|BINDTEST_ABORT_PROGRESS, TYMED_FILE); + lstrcpyW(current_url, url); + + SET_EXPECT(GetBindInfo); + SET_EXPECT(QueryInterface_IInternetProtocol); + SET_EXPECT(QueryInterface_IServiceProvider); + SET_EXPECT(QueryService_IInternetProtocol); + SET_EXPECT(OnStartBinding); + SET_EXPECT(QueryInterface_IHttpNegotiate); + SET_EXPECT(QueryInterface_IHttpNegotiate2); + SET_EXPECT(BeginningTransaction); + SET_EXPECT(GetRootSecurityId); + SET_EXPECT(QueryInterface_IWindowForBindingUI); + SET_EXPECT(OnProgress_CONNECTING); + SET_EXPECT(OnProgress_SENDINGREQUEST); + SET_EXPECT(OnStopBinding); + + hres = URLDownloadToCacheFileW(NULL, current_url, file_name, ARRAY_SIZE(file_name), 0, + (IBindStatusCallback*)&bsc); + ok(hres == E_ABORT, "URLDownloadToCacheFileW failed: %08lx, expected E_ABORT\n", hres); + + CHECK_CALLED(GetBindInfo); + CHECK_CALLED(QueryInterface_IInternetProtocol); + CHECK_CALLED(QueryInterface_IServiceProvider); + CHECK_CALLED(QueryService_IInternetProtocol); + CHECK_CALLED(OnStartBinding); + CHECK_CALLED(QueryInterface_IHttpNegotiate); + CHECK_CALLED(QueryInterface_IHttpNegotiate2); + CHECK_CALLED(BeginningTransaction); + CHECK_CALLED(GetRootSecurityId); + CLEAR_CALLED(QueryInterface_IWindowForBindingUI); + CHECK_CALLED(OnProgress_SENDINGREQUEST); + CLEAR_CALLED(OnProgress_CONNECTING); + CHECK_CALLED(OnStopBinding); + + after = count_cache_files(cache_root, L"urlmon-cache-cleanup-"); + todo_wine ok(after == before, "temporary cache file leaked: %lu files before, %lu after\n", + before, after); +} + static void set_file_url(char *path) { CHAR file_urlA[INTERNET_MAX_URL_LENGTH]; @@ -4213,6 +4324,9 @@ START_TEST(url) trace("test URLDownloadToFile abort...\n"); test_URLDownloadToFile_abort(); + trace("test URLDownloadToCacheFile abort...\n"); + test_URLDownloadToCacheFile_abort(); + trace("test emulated http abort...\n"); test_BindToStorage(HTTP_TEST, BINDTEST_EMULATE|BINDTEST_ABORT, TYMED_ISTREAM); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11331