diff --git a/dlls/uxtheme/tests/msg.h b/dlls/uxtheme/tests/msg.h new file mode 100644 index 00000000000..1be05499fd4 --- /dev/null +++ b/dlls/uxtheme/tests/msg.h @@ -0,0 +1,409 @@ +/* Message Sequence Testing Code + * + * Copyright (C) 2007 James Hawkins + * Copyright (C) 2007 Lei Zhang + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * 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 + */ + +#include +#include +#include "wine/heap.h" +#include "wine/test.h" + +/* undocumented SWP flags - from SDK 3.1 */ +#define SWP_NOCLIENTSIZE 0x0800 +#define SWP_NOCLIENTMOVE 0x1000 + +typedef enum +{ + sent = 0x1, + posted = 0x2, + parent = 0x4, + wparam = 0x8, + lparam = 0x10, + defwinproc = 0x20, + beginpaint = 0x40, + optional = 0x80, + hook = 0x100, + winevent_hook =0x200, + id = 0x400, + custdraw = 0x800 +} msg_flags_t; + +struct message +{ + UINT message; /* the WM_* code */ + msg_flags_t flags; /* message props */ + WPARAM wParam; /* expected value of wParam */ + LPARAM lParam; /* expected value of lParam */ + UINT id; /* extra message data: id of the window, + notify code etc. */ + DWORD stage; /* custom draw stage */ +}; + +struct msg_sequence +{ + int count; + int size; + struct message *sequence; +}; + +static void add_message(struct msg_sequence **seq, int sequence_index, + const struct message *msg) +{ + struct msg_sequence *msg_seq = seq[sequence_index]; + + if (!msg_seq->sequence) + { + msg_seq->size = 10; + msg_seq->sequence = heap_alloc(msg_seq->size * sizeof (*msg_seq->sequence)); + } + + if (msg_seq->count == msg_seq->size) + { + msg_seq->size *= 2; + msg_seq->sequence = heap_realloc(msg_seq->sequence, msg_seq->size * sizeof (*msg_seq->sequence)); + } + + assert(msg_seq->sequence); + + msg_seq->sequence[msg_seq->count] = *msg; + msg_seq->count++; +} + +static inline void flush_sequence(struct msg_sequence **seg, int sequence_index) +{ + struct msg_sequence *msg_seq = seg[sequence_index]; + heap_free(msg_seq->sequence); + msg_seq->sequence = NULL; + msg_seq->count = msg_seq->size = 0; +} + +static inline void flush_sequences(struct msg_sequence **seq, int n) +{ + int i; + + for (i = 0; i < n; i++) + flush_sequence(seq, i); +} + +static void dump_sequence( struct msg_sequence **seq, int sequence_index, + const struct message *expected, const char *context, + const char *file, int line ) +{ + struct msg_sequence *msg_seq = seq[sequence_index]; + const struct message *actual, *sequence; + unsigned int count = 0; + + sequence = msg_seq->sequence; + actual = sequence; + + trace_(file, line)("Failed sequence %s:\n", context ); + while (expected->message && actual->message) + { + trace_(file, line)( " %u: expected: %04x - actual: %04x wp %08lx lp %08lx\n", + count, expected->message, actual->message, actual->wParam, actual->lParam ); + + if (expected->message == actual->message) + { + if ((expected->flags & defwinproc) != (actual->flags & defwinproc) && + (expected->flags & optional)) + { + /* don't match messages if their defwinproc status differs */ + expected++; + } + else + { + expected++; + actual++; + } + } + else + { + expected++; + actual++; + } + count++; + } + + /* optional trailing messages */ + while (expected->message && expected->flags & optional) + { + trace_(file, line)( " %u: expected: msg %04x - actual: nothing\n", count, expected->message ); + expected++; + count++; + } + + if (expected->message) + { + trace_(file, line)( " %u: expected: msg %04x - actual: nothing\n", count, expected->message ); + return; + } + + while (actual->message) + { + trace_(file, line)( " %u: expected: nothing - actual: %04x wp %08lx lp %08lx\n", + count, actual->message, actual->wParam, actual->lParam ); + actual++; + count++; + } +} + +static void ok_sequence_(struct msg_sequence **seq, int sequence_index, + const struct message *expected_list, const char *context, BOOL todo, + const char *file, int line) +{ + static const struct message end_of_sequence = {0, 0, 0, 0}; + struct msg_sequence *msg_seq = seq[sequence_index]; + const struct message *expected = expected_list; + const struct message *actual, *sequence; + int failcount = 0, dump = 0; + + add_message(seq, sequence_index, &end_of_sequence); + + sequence = msg_seq->sequence; + actual = sequence; + + while (expected->message && actual->message) + { + if (expected->message == actual->message) + { + if (expected->flags & wparam) + { + if (expected->wParam != actual->wParam && todo) + { + todo_wine + { + failcount++; + dump++; + ok_(file, line) (FALSE, + "%s: in msg 0x%04x expecting wParam 0x%lx got 0x%lx\n", + context, expected->message, expected->wParam, actual->wParam); + } + } + else + { + ok_(file, line) (expected->wParam == actual->wParam, + "%s: in msg 0x%04x expecting wParam 0x%lx got 0x%lx\n", + context, expected->message, expected->wParam, actual->wParam); + if (expected->wParam != actual->wParam) dump++; + } + } + + if (expected->flags & lparam) + { + if (expected->lParam != actual->lParam && todo) + { + todo_wine + { + failcount++; + dump++; + ok_(file, line) (FALSE, + "%s: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n", + context, expected->message, expected->lParam, actual->lParam); + } + } + else + { + ok_(file, line) (expected->lParam == actual->lParam, + "%s: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n", + context, expected->message, expected->lParam, actual->lParam); + if (expected->lParam != actual->lParam) dump++; + } + } + + if (expected->flags & custdraw) + { + if (expected->stage != actual->stage && todo) + { + todo_wine + { + failcount++; + dump++; + ok_(file, line) (FALSE, + "%s: in msg 0x%04x expecting cd stage 0x%08x got 0x%08x\n", + context, expected->message, expected->stage, actual->stage); + } + } + else + { + ok_(file, line) (expected->stage == actual->stage, + "%s: in msg 0x%04x expecting cd stage 0x%08x got 0x%08x\n", + context, expected->message, expected->stage, actual->stage); + if (expected->stage != actual->stage) dump++; + } + } + + if (expected->flags & id) + { + if (expected->id != actual->id && expected->flags & optional) + { + expected++; + continue; + } + if (expected->id != actual->id && todo) + { + todo_wine + { + failcount++; + dump++; + ok_(file, line) (FALSE, + "%s: in msg 0x%04x expecting id 0x%x got 0x%x\n", + context, expected->message, expected->id, actual->id); + } + } + else + { + ok_(file, line) (expected->id == actual->id, + "%s: in msg 0x%04x expecting id 0x%x got 0x%x\n", + context, expected->message, expected->id, actual->id); + if (expected->id != actual->id) dump++; + } + } + + if ((expected->flags & defwinproc) != (actual->flags & defwinproc) && todo) + { + todo_wine + { + failcount++; + dump++; + ok_(file, line) (FALSE, + "%s: the msg 0x%04x should %shave been sent by DefWindowProc\n", + context, expected->message, (expected->flags & defwinproc) ? "" : "NOT "); + } + } + else + { + ok_(file, line) ((expected->flags & defwinproc) == (actual->flags & defwinproc), + "%s: the msg 0x%04x should %shave been sent by DefWindowProc\n", + context, expected->message, (expected->flags & defwinproc) ? "" : "NOT "); + if ((expected->flags & defwinproc) != (actual->flags & defwinproc)) dump++; + } + + ok_(file, line) ((expected->flags & beginpaint) == (actual->flags & beginpaint), + "%s: the msg 0x%04x should %shave been sent by BeginPaint\n", + context, expected->message, (expected->flags & beginpaint) ? "" : "NOT "); + if ((expected->flags & beginpaint) != (actual->flags & beginpaint)) dump++; + + if (((expected->flags & (sent|posted)) != (actual->flags & (sent|posted))) && todo) + { + todo_wine + { + failcount++; + dump++; + ok_(file, line) (FALSE, + "%s: the msg 0x%04x should have been %s\n", + context, expected->message, (expected->flags & posted) ? "posted" : "sent"); + } + } + else + { + ok_(file, line) ((expected->flags & (sent|posted)) == (actual->flags & (sent|posted)), + "%s: the msg 0x%04x should have been %s\n", + context, expected->message, (expected->flags & posted) ? "posted" : "sent"); + if ((expected->flags & (sent|posted)) != (actual->flags & (sent|posted))) dump++; + } + + ok_(file, line) ((expected->flags & parent) == (actual->flags & parent), + "%s: the msg 0x%04x was expected in %s\n", + context, expected->message, (expected->flags & parent) ? "parent" : "child"); + if ((expected->flags & parent) != (actual->flags & parent)) dump++; + + ok_(file, line) ((expected->flags & hook) == (actual->flags & hook), + "%s: the msg 0x%04x should have been sent by a hook\n", + context, expected->message); + if ((expected->flags & hook) != (actual->flags & hook)) dump++; + + ok_(file, line) ((expected->flags & winevent_hook) == (actual->flags & winevent_hook), + "%s: the msg 0x%04x should have been sent by a winevent hook\n", + context, expected->message); + if ((expected->flags & winevent_hook) != (actual->flags & winevent_hook)) dump++; + + expected++; + actual++; + } + else if (expected->flags & optional) + expected++; + else if (todo) + { + failcount++; + dump++; + todo_wine + { + ok_(file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n", + context, expected->message, actual->message); + } + goto done; + } + else + { + ok_(file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n", + context, expected->message, actual->message); + dump++; + expected++; + actual++; + } + } + + /* skip all optional trailing messages */ + while (expected->message && ((expected->flags & optional))) + expected++; + + if (todo) + { + todo_wine + { + if (expected->message || actual->message) + { + failcount++; + dump++; + ok_(file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n", + context, expected->message, actual->message); + } + } + } + else if (expected->message || actual->message) + { + dump++; + ok_(file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n", + context, expected->message, actual->message); + } + + if(todo && !failcount) /* succeeded yet marked todo */ + { + if (!strcmp(winetest_platform, "wine")) dump++; + todo_wine + { + ok_(file, line)(TRUE, "%s: marked \"todo_wine\" but succeeds\n", context); + } + } + +done: + if (dump) dump_sequence( seq, sequence_index, expected_list, context, file, line ); + flush_sequence(seq, sequence_index); +} + +#define ok_sequence(seq, index, exp, contx, todo) \ + ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__) + + +static void init_msg_sequences(struct msg_sequence **seq, int n) +{ + int i; + + for (i = 0; i < n; i++) + seq[i] = heap_alloc_zero(sizeof(*seq[i])); +} diff --git a/dlls/uxtheme/tests/system.c b/dlls/uxtheme/tests/system.c index 869dbe2cdc9..38cccb3ec6f 100644 --- a/dlls/uxtheme/tests/system.c +++ b/dlls/uxtheme/tests/system.c @@ -24,6 +24,7 @@ #include "vfwmsgs.h" #include "uxtheme.h" +#include "msg.h" #include "wine/test.h" static HTHEME (WINAPI * pOpenThemeDataEx)(HWND, LPCWSTR, DWORD); @@ -35,6 +36,16 @@ static HDC (WINAPI *pGetBufferedPaintDC)(HPAINTBUFFER); static HDC (WINAPI *pGetBufferedPaintTargetDC)(HPAINTBUFFER); static HRESULT (WINAPI *pGetBufferedPaintTargetRect)(HPAINTBUFFER, RECT *); +/* For message tests */ +enum seq_index +{ + PARENT_SEQ_INDEX, + CHILD_SEQ_INDEX, + NUM_MSG_SEQUENCES +}; + +static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; + static void init_funcs(void) { HMODULE hUxtheme = GetModuleHandleA("uxtheme.dll"); @@ -53,6 +64,72 @@ static void init_funcs(void) #undef UXTHEME_GET_PROC } +/* Try to make sure pending X events have been processed before continuing */ +static void flush_events(void) +{ + MSG msg; + int diff = 200; + int min_timeout = 100; + DWORD time = GetTickCount() + diff; + + while (diff > 0) + { + if (MsgWaitForMultipleObjects(0, NULL, FALSE, min_timeout, QS_ALLINPUT) == WAIT_TIMEOUT) + break; + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) + DispatchMessageA(&msg); + diff = time - GetTickCount(); + } +} + +static LRESULT WINAPI TestSetWindowThemeParentProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static LONG defwndproc_counter; + struct message msg = {0}; + LRESULT ret; + + /* Only care about WM_THEMECHANGED */ + if (message != WM_THEMECHANGED) + return DefWindowProcA(hwnd, message, wParam, lParam); + + msg.message = message; + msg.flags = sent | wparam | lparam; + if (defwndproc_counter) + msg.flags |= defwinproc; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(sequences, PARENT_SEQ_INDEX, &msg); + + InterlockedIncrement(&defwndproc_counter); + ret = DefWindowProcA(hwnd, message, wParam, lParam); + InterlockedDecrement(&defwndproc_counter); + return ret; +} + +static LRESULT WINAPI TestSetWindowThemeChildProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static LONG defwndproc_counter; + struct message msg = {0}; + LRESULT ret; + + /* Only care about WM_THEMECHANGED */ + if (message != WM_THEMECHANGED) + return DefWindowProcA(hwnd, message, wParam, lParam); + + msg.message = message; + msg.flags = sent | wparam | lparam; + if (defwndproc_counter) + msg.flags |= defwinproc; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(sequences, CHILD_SEQ_INDEX, &msg); + + InterlockedIncrement(&defwndproc_counter); + ret = DefWindowProcA(hwnd, message, wParam, lParam); + InterlockedDecrement(&defwndproc_counter); + return ret; +} + static void test_IsThemed(void) { BOOL bThemeActive; @@ -97,15 +174,71 @@ static void test_GetWindowTheme(void) DestroyWindow(hWnd); } +static const struct message SetWindowThemeSeq[] = +{ + {WM_THEMECHANGED, sent}, + {0} +}; + +static const struct message EmptySeq[] = +{ + {0} +}; + static void test_SetWindowTheme(void) { - HTHEME hTheme; + WNDCLASSA cls = {0}; + HWND hWnd, child; + HTHEME hTheme; HRESULT hRes; - HWND hWnd; + MSG msg; hRes = SetWindowTheme(NULL, NULL, NULL); ok( hRes == E_HANDLE, "Expected E_HANDLE, got 0x%08x\n", hRes); + /* Test that WM_THEMECHANGED is sent and not posted to windows */ + cls.hInstance = GetModuleHandleA(0); + cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpfnWndProc = TestSetWindowThemeParentProcA; + cls.lpszClassName = "TestSetWindowThemeParentClass"; + RegisterClassA(&cls); + + cls.lpfnWndProc = TestSetWindowThemeChildProcA; + cls.lpszClassName = "TestSetWindowThemeChildClass"; + RegisterClassA(&cls); + + hWnd = CreateWindowA("TestSetWindowThemeParentClass", "parent", + WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 200, 200, 0, 0, 0, NULL); + ok(!!hWnd, "Failed to create a parent window.\n"); + child = CreateWindowA("TestSetWindowThemeChildClass", "child", WS_CHILD | WS_VISIBLE, 0, 0, 50, + 50, hWnd, 0, 0, NULL); + ok(!!child, "Failed to create a child window.\n"); + flush_events(); + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + hRes = SetWindowTheme(hWnd, NULL, NULL); + ok(hRes == S_OK, "Expected %#x, got %#x.\n", S_OK, hRes); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) + { + struct message recv_msg = {0}; + + if (msg.message == WM_THEMECHANGED) + { + recv_msg.message = msg.message; + recv_msg.flags = posted | wparam | lparam; + recv_msg.wParam = msg.wParam; + recv_msg.lParam = msg.lParam; + add_message(sequences, msg.hwnd == hWnd ? PARENT_SEQ_INDEX : CHILD_SEQ_INDEX, &recv_msg); + } + DispatchMessageA(&msg); + } + ok_sequence(sequences, PARENT_SEQ_INDEX, SetWindowThemeSeq, "SetWindowTheme parent", TRUE); + ok_sequence(sequences, CHILD_SEQ_INDEX, EmptySeq, "SetWindowTheme child", TRUE); + DestroyWindow(hWnd); + UnregisterClassA("TestSetWindowThemeParentClass", GetModuleHandleA(0)); + UnregisterClassA("TestSetWindowThemeChildClass", GetModuleHandleA(0)); + /* Only do the bare minimum to get a valid hwnd */ hWnd = CreateWindowExA(0, "button", "test", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, NULL); ok(hWnd != NULL, "Failed to create a test window.\n"); @@ -697,6 +830,7 @@ todo_wine START_TEST(system) { init_funcs(); + init_msg_sequences(sequences, NUM_MSG_SEQUENCES); /* No real functional theme API tests will be done (yet). The current tests * only show input/return behaviour