From 01ca4d54401fa5d8226a28153caf24c5b4747e17 Mon Sep 17 00:00:00 2001 From: Daniel Santos Date: Fri, 10 Jul 2009 14:57:04 -0500 Subject: user32: Add more tests for SetCursor & DestroyCursor --- dlls/user32/tests/cursoricon.c | 291 +++++++++++++++++++++++++++++++++------- 1 files changed, 241 insertions(+), 50 deletions(-) diff --git a/dlls/user32/tests/cursoricon.c b/dlls/user32/tests/cursoricon.c index 3f8bd82..f69a64f 100644 --- a/dlls/user32/tests/cursoricon.c +++ b/dlls/user32/tests/cursoricon.c @@ -18,6 +18,12 @@ * 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 + * + * FIXME: + * - Add tests for CreateCursor and show that width & height can exceed + * SM_CXCURSOR & SM_CYCURSOR (as the current wine implementation will allow + * this). + * - Add tests for DestroyIcon32. */ #include @@ -64,6 +70,132 @@ static HANDLE child_process; #define PROC_INIT (WM_USER+1) +static inline int is_win9x() { + SetLastError(0xdeadbeef); + lstrcmpW(NULL, NULL); + return (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED); +} + +/************************************************************************* + * test_SetCursor + * + * Encapsulates tests to SetCursor() function. + * + * PARAMS + * line [I] The line this function was called from. + * target [I] A handle to use when calling SetCursor + * shouldFail [I] Rather of not the call to SetCursor should fail + * retValTodo [I] True if return value is broken in wine (wine's return value + * is broken in some, but not all cases) + * + * RETURNS + * The return value of the SetCursor() call. + */ +#define test_SetCursor(a, b, c) _test_SetCursor(__LINE__, a, b, c) +static HCURSOR _test_SetCursor(int line, HANDLE hNewCursor, BOOL shouldFail, + BOOL retValTodo) +{ + HCURSOR cursor1, cursor2, cursor3; + DWORD error; + + /* GetCursor should have no errors */ + SetLastError(0xdeadbeef); + cursor1 = GetCursor(); + error = GetLastError(); + ok_(__FILE__, line)(error == 0xdeadbeef, + "GetCursor() changed last error: %u.\n", + error); + + /* Call SetCursor on supplied handle */ + SetLastError(0xdeadbeef); + cursor2 = SetCursor(hNewCursor); + error = GetLastError(); + cursor3 = GetCursor(); + + if (shouldFail) { + todo_wine ok_(__FILE__, line)(error == ERROR_INVALID_CURSOR_HANDLE, + "Last error is %u (0x%x), expected " + "ERROR_INVALID_CURSOR_HANDLE (%u).\n", + error, error, ERROR_INVALID_CURSOR_HANDLE); + + if(retValTodo) { + todo_wine ok_(__FILE__, line)(!cursor2, + "SetCursor() returned non-zero (%p).\n", + cursor2); + } else { + ok_(__FILE__, line)(!cursor2, + "SetCursor() returned non-zero (%p).\n", + cursor2); + } + + todo_wine ok_(__FILE__, line)(cursor1 == cursor3, + "SetCursor() changed cursor.\n"); + } else { + /* WinXP: For some reason, SetCursor() is returning NULL when hNewCursor + * is NULL. Is it this way on other versions? MSDN says it should + * return the old cursor. + */ + if(hNewCursor) { + ok_(__FILE__, line)(cursor1 == cursor2, + "SetCursor() did not return the previous cursor, " + "expected %p, got %p.\n", + cursor1, cursor2); + } else { + todo_wine ok_(__FILE__, line)(!cursor2, + "SetCursor() returned %p, expected NULL.\n", + cursor2); + } + + ok_(__FILE__, line)(cursor3 == hNewCursor, + "GetCursor() did not return the value we previously passed to " + "SetCursor(). Expected %p, got %p.\n", + hNewCursor, cursor3); + } + + return cursor2; +} + + +/************************************************************************* + * test_DestroyCursorIcon + * + * Encapsulates simple tests to DestroyCursor and DestroyIcon functions. + * + * PARAMS + * line [I] The line this function was called from. + * h [I] A handle to use when calling DestroyIcon/DestroyCursor + * isCursor [I] TRUE for DestroyCursor, FALSE for DestroyIcon + * expectedRet [I] The expected return value + * expectedError [I] The expected last error + * + * RETURNS + * The return value of the function call. + */ +#define test_DestroyCursorIcon(a, b, c, d) \ + _test_DestroyCursorIcon(__LINE__, a, b, c, d) +static BOOL _test_DestroyCursorIcon(int line, HANDLE h, BOOL isCursor, + BOOL expectedRet, DWORD expectedError) +{ + BOOL ret; + DWORD error; + const char* funcName = isCursor ? "DestroyCursor" : "DestroyIcon"; + + SetLastError(0xdeadbeef); + ret = isCursor ? DestroyCursor(h) : DestroyIcon(h); + error = GetLastError(); + + /* broken on NT4 Server SP6a when handle == 1 */ + ok_(__FILE__, line)(ret == expectedRet || broken(h == (HANDLE)1), + "%s returned %d, expected %s.\n", + funcName, ret, expectedRet ? "TRUE" : "FALSE"); + ok_(__FILE__, line)(error == expectedError || broken(h == (HANDLE)1), + "Last error is %u (0x%x), expected %u (0x%x).\n", + error, error, expectedError, expectedError); + + return ret; +} + + static LRESULT CALLBACK callback_child(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { BOOL ret; @@ -76,10 +208,11 @@ static LRESULT CALLBACK callback_child(HWND hwnd, UINT msg, WPARAM wParam, LPARA SetLastError(0xdeadbeef); ret = DestroyCursor((HCURSOR) lParam); error = GetLastError(); - todo_wine ok(!ret || broken(ret) /* win9x */, "DestroyCursor on the active cursor succeeded.\n"); + todo_wine ok(!ret || broken(ret), /* win9x */ + "DestroyCursor on the active cursor of another process succeeded.\n"); ok(error == ERROR_DESTROY_OBJECT_OF_OTHER_THREAD || - error == 0xdeadbeef, /* vista */ - "Last error: %u\n", error); + error == 0xdeadbeef, /* vista */ + "Last error: %u\n", error); return TRUE; case WM_DESTROY: PostQuitMessage(0); @@ -169,6 +302,18 @@ static void do_parent(void) 0, 0, 200, 200, 0, 0, 0, NULL); ok(parent != 0, "CreateWindowA failed. Error: %u\n", GetLastError()); + /* make sure Destroy{Cursor,Icon} wont accept other user handle types */ + if(is_win9x()) { + win_skip("Invalid cursor handles will crash win9x\n"); + } else { + todo_wine { + test_DestroyCursorIcon(parent, TRUE, FALSE, + ERROR_INVALID_CURSOR_HANDLE); + test_DestroyCursorIcon(parent, FALSE, FALSE, + ERROR_INVALID_CURSOR_HANDLE); + } + } + /* Start child process. */ memset(&startup, 0, sizeof(startup)); startup.cb = sizeof(startup); @@ -217,7 +362,7 @@ static void test_child_process(void) cursor = CreateIconIndirect(&cursorInfo); ok(cursor != NULL, "CreateIconIndirect returned %p.\n", cursor); - SetCursor(cursor); + test_SetCursor(cursor, FALSE, FALSE); /* Destroy the cursor. */ SendMessage(child, WM_USER+1, 0, (LPARAM) cursor); @@ -246,7 +391,7 @@ static void test_CopyImage_Check(HBITMAP bitmap, UINT flags, INT copyWidth, INT && (expectedDepth == 16 || expectedDepth == 32)) { /* Windows 95 doesn't create DIBs with a depth of 16 or 32 bit */ - if (GetVersion() & 0x80000000) + if (is_win9x()) { expectedDepth = 24; } @@ -377,7 +522,7 @@ static void test_CopyImage_Bitmap(int depth) Skip this test on Windows 95, it always creates a monochrome DDB in this case */ - if (!(GetVersion() & 0x80000000)) + if (!is_win9x()) { info->bmiHeader.biBitCount = 1; info->bmiColors[0].rgbRed = 0xFF; @@ -520,12 +665,12 @@ static void test_CreateIcon(void) hIcon = CreateIcon(0, 16, 16, 1, 1, bmp_bits, bmp_bits); ok(hIcon != 0, "CreateIcon failed\n"); test_icon_info(hIcon, 16, 16, 1); - DestroyIcon(hIcon); + test_DestroyCursorIcon(hIcon, FALSE, TRUE, 0xdeadbeef); hIcon = CreateIcon(0, 16, 16, 1, display_bpp, bmp_bits, bmp_bits); ok(hIcon != 0, "CreateIcon failed\n"); test_icon_info(hIcon, 16, 16, display_bpp); - DestroyIcon(hIcon); + test_DestroyCursorIcon(hIcon, FALSE, TRUE, 0xdeadbeef); hbmMask = CreateBitmap(16, 16, 1, 1, bmp_bits); ok(hbmMask != 0, "CreateBitmap failed\n"); @@ -560,7 +705,7 @@ static void test_CreateIcon(void) hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); test_icon_info(hIcon, 16, 16, display_bpp); - DestroyIcon(hIcon); + test_DestroyCursorIcon(hIcon, FALSE, TRUE, 0xdeadbeef); DeleteObject(hbmMask); DeleteObject(hbmColor); @@ -577,7 +722,7 @@ static void test_CreateIcon(void) hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); test_icon_info(hIcon, 16, 16, 1); - DestroyIcon(hIcon); + test_DestroyCursorIcon(hIcon, FALSE, TRUE, 0xdeadbeef); DeleteObject(hbmMask); DeleteObject(hbmColor); @@ -610,7 +755,7 @@ static void test_CreateIcon(void) hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); test_icon_info(hIcon, 32, 32, 8); - DestroyIcon(hIcon); + test_DestroyCursorIcon(hIcon, FALSE, TRUE, 0xdeadbeef); DeleteObject(hbmColor); bmpinfo->bmiHeader.biBitCount = 16; @@ -628,7 +773,7 @@ static void test_CreateIcon(void) hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); test_icon_info(hIcon, 32, 32, 8); - DestroyIcon(hIcon); + test_DestroyCursorIcon(hIcon, FALSE, TRUE, 0xdeadbeef); DeleteObject(hbmColor); bmpinfo->bmiHeader.biBitCount = 32; @@ -646,7 +791,7 @@ static void test_CreateIcon(void) hIcon = CreateIconIndirect(&info); ok(hIcon != 0, "CreateIconIndirect failed\n"); test_icon_info(hIcon, 32, 32, 8); - DestroyIcon(hIcon); + test_DestroyCursorIcon(hIcon, FALSE, TRUE, 0xdeadbeef); DeleteObject(hbmMask); DeleteObject(hbmColor); @@ -740,7 +885,8 @@ static void test_LoadImageFile(const unsigned char * image_data, broken(error == 0xdeadbeef) || /* Win9x */ broken(error == ERROR_BAD_PATHNAME), /* Win98, WinMe */ "Last error: %u\n", error); - if (handle != NULL) DestroyCursor(handle); + if (handle != NULL) + test_DestroyCursorIcon(handle, TRUE, TRUE, 0xdeadbeef); /* Load as icon. For all tested formats, this should fail */ SetLastError(0xdeadbeef); @@ -751,7 +897,8 @@ static void test_LoadImageFile(const unsigned char * image_data, broken(error == 0xdeadbeef) || /* Win9x */ broken(error == ERROR_BAD_PATHNAME), /* Win98, WinMe */ "Last error: %u\n", error); - if (handle != NULL) DestroyIcon(handle); + if (handle != NULL) + test_DestroyCursorIcon(handle, FALSE, TRUE, 0xdeadbeef); /* Load as bitmap. Should succeed if bmp, fail for everything else */ SetLastError(0xdeadbeef); @@ -771,6 +918,7 @@ static void test_LoadImageFile(const unsigned char * image_data, static void test_LoadImage(void) { HANDLE handle; + HCURSOR cursor; BOOL ret; DWORD error, bytes_written; CURSORICONFILEDIR *icon_data; @@ -845,16 +993,26 @@ static void test_LoadImage(void) ok(icon_info.hbmMask != NULL, "No hbmMask!\n"); } + /* Test SetCursor with this icon */ + cursor = test_SetCursor(handle, FALSE, TRUE); + + /* Now change it back to the previous cursor */ + test_SetCursor(cursor, FALSE, FALSE); + /* Clean up. */ - SetLastError(0xdeadbeef); - ret = DestroyCursor(handle); - ok(ret, "DestroyCursor() failed.\n"); - error = GetLastError(); - ok(error == 0xdeadbeef, "Last error: %u\n", error); + test_DestroyCursorIcon(handle, TRUE, TRUE, 0xdeadbeef); HeapFree(GetProcessHeap(), 0, icon_data); DeleteFileA("icon.ico"); + /* Test passing invalid handles to SetCursor. */ + if(is_win9x()) { + win_skip("Invalid cursor handles will crash win9x\n"); + } else { + test_SetCursor(handle, TRUE, FALSE); + test_SetCursor(GetCurrentThread(), TRUE, TRUE); + } + test_LoadImageFile(bmpimage, sizeof(bmpimage), "bmp", 1); test_LoadImageFile(gifimage, sizeof(gifimage), "gif", 0); test_LoadImageFile(gif4pixel, sizeof(gif4pixel), "gif", 0); @@ -917,11 +1075,7 @@ static void test_CreateIconFromResource(void) } /* Clean up. */ - SetLastError(0xdeadbeef); - ret = DestroyCursor(handle); - ok(ret, "DestroyCursor() failed.\n"); - error = GetLastError(); - ok(error == 0xdeadbeef, "Last error: %u\n", error); + test_DestroyCursorIcon(handle, TRUE, TRUE, 0xdeadbeef); /* Test creating an icon. */ SetLastError(0xdeadbeef); @@ -947,11 +1101,7 @@ static void test_CreateIconFromResource(void) } /* Clean up. */ - SetLastError(0xdeadbeef); - ret = DestroyCursor(handle); - ok(ret, "DestroyCursor() failed.\n"); - error = GetLastError(); - ok(error == 0xdeadbeef, "Last error: %u\n", error); + test_DestroyCursorIcon(handle, TRUE, TRUE, 0xdeadbeef); HeapFree(GetProcessHeap(), 0, hotspot); } @@ -1222,6 +1372,26 @@ static void test_DestroyCursor(void) UINT display_bpp; HDC hdc; + /* Try to destroy various invalid handles */ + if(is_win9x()) { + win_skip("Invalid cursor handles will crash win9x\n"); + } else { + todo_wine { + test_DestroyCursorIcon(0, TRUE, FALSE, + ERROR_INVALID_CURSOR_HANDLE); + test_DestroyCursorIcon((HANDLE)1, TRUE, FALSE, + ERROR_INVALID_CURSOR_HANDLE); + test_DestroyCursorIcon(INVALID_HANDLE_VALUE, TRUE, FALSE, + ERROR_INVALID_CURSOR_HANDLE); + test_DestroyCursorIcon(0, FALSE, FALSE, + ERROR_INVALID_CURSOR_HANDLE); + test_DestroyCursorIcon((HANDLE)1, FALSE, FALSE, + ERROR_INVALID_CURSOR_HANDLE); + test_DestroyCursorIcon(INVALID_HANDLE_VALUE, FALSE, FALSE, + ERROR_INVALID_CURSOR_HANDLE); + } + } + hdc = GetDC(0); display_bpp = GetDeviceCaps(hdc, BITSPIXEL); ReleaseDC(0, hdc); @@ -1237,29 +1407,54 @@ static void test_DestroyCursor(void) if(!cursor) { return; } - SetCursor(cursor); + test_SetCursor(cursor, FALSE, FALSE); + + /* When you attempt to destroy the current cursor, windows obliges you. + * On win9x, it returns TRUE (indicating success) and on NT it returns + * FALSE, but neither sets the last error. + */ SetLastError(0xdeadbeef); ret = DestroyCursor(cursor); - ok(!ret || broken(ret) /* succeeds on win9x */, "DestroyCursor on the active cursor succeeded\n"); error = GetLastError(); - ok(error == 0xdeadbeef, "Last error: %u\n", error); + ok(error == 0xdeadbeef, "Last error: 0x%x.\n", error); + ok(!ret || broken(ret) /* succeeds on win9x */, + "DestroyCursor on the active cursor returned TRUE.\n"); + + /* QUESTION: This if statment effectively skips these tests on win9x, do we + * want to do that? */ if (!ret) { cursor2 = GetCursor(); - ok(cursor2 == cursor, "Active was set to %p when trying to destroy it\n", cursor2); - SetCursor(NULL); - - /* Trying to destroy the cursor properly fails now with - * ERROR_INVALID_CURSOR_HANDLE. This happens because we called - * DestroyCursor() 2+ times after calling SetCursor(). The calls to - * GetCursor() and SetCursor(NULL) in between make no difference. */ + ok(cursor2 == cursor, "Active cursor was set to %p when trying to " + "destroy it, expected it to be %p.\n", cursor2, cursor); + + /* Make sure showing/hiding the invalid cursor still works. */ + SetLastError(0xdeadbeef); + ok(ShowCursor(TRUE) == 1, "Cursor count != 1\n"); + ok(GetLastError() == 0xdeadbeef, "Last error: 0x%x.\n", GetLastError()); + + SetLastError(0xdeadbeef); + ok(ShowCursor(FALSE) == 0, "Cursor count != 0\n"); + ok(GetLastError() == 0xdeadbeef, "Last error: 0x%x.\n", GetLastError()); + test_SetCursor(NULL, FALSE, FALSE); + + /* Trying to destroy the cursor again fails now with + * ERROR_INVALID_CURSOR_HANDLE. This happens because we've previously + * called DestroyCursor() on this handle. The calls to GetCursor() + * and SetCursor(NULL) in between make no difference. It would appear + * that windows doesn't mind having an invalid cursor handle as its + * current cursor much the way a comatose person doesn't mind a + * stranger in their hospital room. + */ + SetLastError(0xdeadbeef); ret = DestroyCursor(cursor); todo_wine { ok(!ret, "DestroyCursor succeeded.\n"); error = GetLastError(); - ok(error == ERROR_INVALID_CURSOR_HANDLE || error == 0xdeadbeef, /* vista */ - "Last error: 0x%08x\n", error); + ok(error == ERROR_INVALID_CURSOR_HANDLE + || broken(error == 0xdeadbeef), /* vista */ + "Last error: 0x%08x\n", error); } } @@ -1271,17 +1466,13 @@ static void test_DestroyCursor(void) SetLastError(0xdeadbeef); ret = DestroyCursor(cursor); - ok(ret || broken(!ret) /* fails on win9x */, "DestroyCursor on the active cursor failed.\n"); error = GetLastError(); - ok(error == 0xdeadbeef, "Last error: 0x%08x\n", error); + ok(error == 0xdeadbeef, "Last error: 0x%x.\n", error); + ok(ret || broken(!ret) /* fails on win9x */, + "DestroyCursor on OEM cursor failed.\n"); /* Try setting the cursor to a destroyed OEM cursor. */ - SetLastError(0xdeadbeef); - SetCursor(cursor); - error = GetLastError(); - todo_wine { - ok(error == 0xdeadbeef, "Last error: 0x%08x\n", error); - } + test_SetCursor(cursor, FALSE, FALSE); /* Check if LoadCursor() returns the same handle with the same icon. */ cursor2 = LoadCursor(NULL, IDC_ARROW); -- 1.6.3.3