Thanks again for your feedback and assistance.  This is my 4th revision. I've 
created a static inline is_win9x() as you suggested.  I agree that this should 
be in include/wine/test.h.  One minor complication is that some test files 
define a BOOL or int "is_win9x" local variable, but that is easy to overcome.  
A few months ago, I wrote an extensive windows version testing patch for 
wine/test.h, but I appear to have either mis-placed it or accidentally blown it 
away! =)

I've added win_skip() messages where tests are skipped on win9x to avoid 
crashing the and I clarified the FIXME (I hope it makes more sense).  I'm 
trying to say that we should add tests to make sure height & width are checked 
since CreateCursor currently doesn't validate the size of the cursor you want 
to create against the values returned by GetSystemMetrics().

Finally, by Griswold's suggestion, I renamed test_SetCursor and 
test_DestroyCursor, adding an underscore prefix and a macro that will fill in 
the __LINE__ to clean up the code slightly.

If you can please run this again on a 9x platform and also an NT platform (XP 
or vista) I would greatly appreciate it!  I just want to make sure everything 
is happy before submitting it and thanks again for all your help!

Daniel




      
From 01ca4d54401fa5d8226a28153caf24c5b4747e17 Mon Sep 17 00:00:00 2001
From: Daniel Santos <[email protected]>
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 <assert.h>
@@ -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



Reply via email to