Ok, I hope this one is stable on win9x. Please give it a try and let me know.
Thanks
Daniel
From 0a94da00ad755ee74a4c3efbe9fefa6161938537 Mon Sep 17 00:00:00 2001
From: Daniel Santos <[email protected]>
Date: Mon, 6 Jul 2009 14:17:39 -0500
Subject: user32: Add more tests for SetCursor & DestroyCursor
---
dlls/user32/tests/cursoricon.c | 276 +++++++++++++++++++++++++++++++++-------
1 files changed, 227 insertions(+), 49 deletions(-)
diff --git a/dlls/user32/tests/cursoricon.c b/dlls/user32/tests/cursoricon.c
index 3f8bd82..3e52691 100644
--- a/dlls/user32/tests/cursoricon.c
+++ b/dlls/user32/tests/cursoricon.c
@@ -18,6 +18,11 @@
* 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 verify that width & height cannot exceed
+ * SM_CXCURSOR & SM_CYCURSOR (because they can).
+ * - Add tests for DestroyIcon32.
*/
#include <assert.h>
@@ -64,6 +69,127 @@ static HANDLE child_process;
#define PROC_INIT (WM_USER+1)
+static inline int is_win9x() {
+ return GetVersion() & 0x80000000;
+}
+
+/*************************************************************************
+ * test_SetCursor
+ *
+ * Encapsulates tests to SetCursor() function.
+ *
+ * PARAMS
+ * target [I] A handle to use when calling SetCursor
+ * shouldFail [I] Rather of not the call to SetCursor should fail
+ * line [I] The line this function was called from.
+ * 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.
+ */
+static HCURSOR test_SetCursor(HANDLE hNewCursor, BOOL shouldFail, int line,
+ 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
+ * h [I] A handle to use when calling DestroyIcon/DestroyCursor
+ * isCursor [I] TRUE for DestroyCursor, FALSE for DestroyIcon
+ * expectedRet [I] The expected return value
+ * line [I] The line this function was called from.
+ * expectedError [I] The expected last error
+ *
+ * RETURNS
+ * The return value of the function call.
+ */
+static BOOL test_DestroyCursorIcon(HANDLE h, BOOL isCursor, BOOL expectedRet,
+ int line, 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;
@@ -72,14 +198,15 @@ static LRESULT CALLBACK callback_child(HWND hwnd, UINT
msg, WPARAM wParam, LPARA
switch (msg)
{
/* Destroy the cursor. */
- case WM_USER+1:
+ case WM_USER + 1:
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 +296,16 @@ 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 */
+ todo_wine {
+ if(!is_win9x()) { /* crashes win 9x */
+ test_DestroyCursorIcon(parent, TRUE, FALSE, __LINE__,
+ ERROR_INVALID_CURSOR_HANDLE);
+ test_DestroyCursorIcon(parent, FALSE, FALSE, __LINE__,
+ ERROR_INVALID_CURSOR_HANDLE);
+ }
+ }
+
/* Start child process. */
memset(&startup, 0, sizeof(startup));
startup.cb = sizeof(startup);
@@ -217,7 +354,7 @@ static void test_child_process(void)
cursor = CreateIconIndirect(&cursorInfo);
ok(cursor != NULL, "CreateIconIndirect returned %p.\n", cursor);
- SetCursor(cursor);
+ test_SetCursor(cursor, FALSE, __LINE__, FALSE);
/* Destroy the cursor. */
SendMessage(child, WM_USER+1, 0, (LPARAM) cursor);
@@ -246,7 +383,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 +514,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 +657,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, __LINE__, 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, __LINE__, 0xdeadbeef);
hbmMask = CreateBitmap(16, 16, 1, 1, bmp_bits);
ok(hbmMask != 0, "CreateBitmap failed\n");
@@ -560,7 +697,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, __LINE__, 0xdeadbeef);
DeleteObject(hbmMask);
DeleteObject(hbmColor);
@@ -577,7 +714,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, __LINE__, 0xdeadbeef);
DeleteObject(hbmMask);
DeleteObject(hbmColor);
@@ -610,7 +747,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, __LINE__, 0xdeadbeef);
DeleteObject(hbmColor);
bmpinfo->bmiHeader.biBitCount = 16;
@@ -628,7 +765,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, __LINE__, 0xdeadbeef);
DeleteObject(hbmColor);
bmpinfo->bmiHeader.biBitCount = 32;
@@ -646,7 +783,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, __LINE__, 0xdeadbeef);
DeleteObject(hbmMask);
DeleteObject(hbmColor);
@@ -740,7 +877,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, __LINE__, 0xdeadbeef);
/* Load as icon. For all tested formats, this should fail */
SetLastError(0xdeadbeef);
@@ -751,7 +889,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, __LINE__, 0xdeadbeef);
/* Load as bitmap. Should succeed if bmp, fail for everything else */
SetLastError(0xdeadbeef);
@@ -771,6 +910,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 +985,24 @@ static void test_LoadImage(void)
ok(icon_info.hbmMask != NULL, "No hbmMask!\n");
}
+ /* Test SetCursor with this icon */
+ cursor = test_SetCursor(handle, FALSE, __LINE__, TRUE);
+
+ /* Now change it back to the previous cursor */
+ test_SetCursor(cursor, FALSE, __LINE__, 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, __LINE__, 0xdeadbeef);
HeapFree(GetProcessHeap(), 0, icon_data);
DeleteFileA("icon.ico");
+ /* Test passing invalid handles to SetCursor. */
+ if(!is_win9x()) { /* crashes win 9x */
+ test_SetCursor(handle, TRUE, __LINE__, FALSE);
+ test_SetCursor(GetCurrentThread(), TRUE, __LINE__, TRUE);
+ }
+
test_LoadImageFile(bmpimage, sizeof(bmpimage), "bmp", 1);
test_LoadImageFile(gifimage, sizeof(gifimage), "gif", 0);
test_LoadImageFile(gif4pixel, sizeof(gif4pixel), "gif", 0);
@@ -917,11 +1065,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, __LINE__, 0xdeadbeef);
/* Test creating an icon. */
SetLastError(0xdeadbeef);
@@ -947,11 +1091,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, __LINE__, 0xdeadbeef);
HeapFree(GetProcessHeap(), 0, hotspot);
}
@@ -1222,6 +1362,24 @@ static void test_DestroyCursor(void)
UINT display_bpp;
HDC hdc;
+ /* Try to destroy various invalid handles */
+ todo_wine {
+ if(!is_win9x()) {
+ test_DestroyCursorIcon(0, TRUE, FALSE, __LINE__,
+ ERROR_INVALID_CURSOR_HANDLE);
+ test_DestroyCursorIcon((HANDLE)1, TRUE, FALSE, __LINE__,
+ ERROR_INVALID_CURSOR_HANDLE);
+ test_DestroyCursorIcon(INVALID_HANDLE_VALUE, TRUE, FALSE, __LINE__,
+ ERROR_INVALID_CURSOR_HANDLE);
+ test_DestroyCursorIcon(0, FALSE, FALSE, __LINE__,
+ ERROR_INVALID_CURSOR_HANDLE);
+ test_DestroyCursorIcon((HANDLE)1, FALSE, FALSE, __LINE__,
+ ERROR_INVALID_CURSOR_HANDLE);
+ test_DestroyCursorIcon(INVALID_HANDLE_VALUE, FALSE, FALSE,
__LINE__,
+ ERROR_INVALID_CURSOR_HANDLE);
+ }
+ }
+
hdc = GetDC(0);
display_bpp = GetDeviceCaps(hdc, BITSPIXEL);
ReleaseDC(0, hdc);
@@ -1237,29 +1395,53 @@ static void test_DestroyCursor(void)
if(!cursor) {
return;
}
- SetCursor(cursor);
+ test_SetCursor(cursor, FALSE, __LINE__, 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. */
+ /* 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, __LINE__, 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 +1453,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, __LINE__, FALSE);
/* Check if LoadCursor() returns the same handle with the same icon. */
cursor2 = LoadCursor(NULL, IDC_ARROW);
--
1.6.3.3