1 /* 2 * PROJECT: ReactOS API tests 3 * LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+) 4 * PURPOSE: debugging and analysis of message states 5 * COPYRIGHT: Copyright 2019 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 */ 7 8 #include "precomp.h" 9 #include "undocuser.h" 10 #include "winxx.h" 11 #include <strsafe.h> 12 13 static void MsgDumpPrintf(LPCSTR fmt, ...) 14 { 15 static char s_szText[1024]; 16 va_list va; 17 va_start(va, fmt); 18 StringCbVPrintfA(s_szText, sizeof(s_szText), fmt, va); 19 trace("%s", s_szText); 20 va_end(va); 21 } 22 #define MSGDUMP_TPRINTF MsgDumpPrintf 23 static char s_prefix[16] = ""; 24 #define MSGDUMP_PREFIX s_prefix 25 #include "msgdump.h" /* msgdump.h needs MSGDUMP_TPRINTF and MSGDUMP_PREFIX */ 26 27 typedef enum STAGE_TYPE 28 { 29 STAGE_TYPE_SEQUENCE, 30 STAGE_TYPE_COUNTING 31 } STAGE_TYPE; 32 33 typedef struct STAGE 34 { 35 INT nLine; 36 UINT uParentMsg; 37 INT nLevel; 38 STAGE_TYPE nType; 39 INT iFirstAction; 40 INT nCount; 41 UINT uMessages[10]; 42 INT iActions[10]; 43 INT nCounters[10]; 44 } STAGE; 45 46 /* variables */ 47 static INT s_iStage; 48 static INT s_iStep; 49 static INT s_nLevel; 50 static BOOL s_bNextStage; 51 static INT s_nCounters[10]; 52 static UINT s_msgStack[32]; 53 static const STAGE *s_pStages; 54 static INT s_cStages; 55 56 /* macros */ 57 #define TIMEOUT_TIMER 999 58 #define TOTAL_TIMEOUT (5 * 1000) 59 #define WIDTH 300 60 #define HEIGHT 200 61 #define PARENT_MSG s_msgStack[s_nLevel - 1] 62 63 static void DoInitialize(const STAGE *pStages, INT cStages) 64 { 65 s_iStage = s_iStep = s_nLevel = 0; 66 s_bNextStage = FALSE; 67 ZeroMemory(s_nCounters, sizeof(s_nCounters)); 68 ZeroMemory(s_msgStack, sizeof(s_msgStack)); 69 s_pStages = pStages; 70 s_cStages = cStages; 71 } 72 73 static void DoFinish(void) 74 { 75 ok_int(s_iStage, s_cStages); 76 if (s_iStage != s_cStages) 77 { 78 skip("Some stage(s) skipped (Step: %d)\n", s_iStep); 79 } 80 } 81 82 typedef enum ACTION 83 { 84 ACTION_ZERO = 0, 85 ACTION_FIRSTMINMAX, 86 ACTION_NCCREATE, 87 ACTION_SHOW, 88 ACTION_IME_SETCONTEXT_OPEN, 89 ACTION_IME_NOTIFY_OPEN, 90 ACTION_DESTROY, 91 ACTION_IME_SETCONTEXT_CLOSE, 92 ACTION_IME_NOTIFY_CLOSE, 93 ACTION_HIDE, 94 ACTION_DEACTIVATE, 95 ACTION_ACTIVATE 96 } ACTION; 97 98 static void DoAction(HWND hwnd, INT iAction, WPARAM wParam, LPARAM lParam) 99 { 100 RECT rc; 101 switch (iAction) 102 { 103 case ACTION_ZERO: 104 /* does nothing */ 105 break; 106 case ACTION_FIRSTMINMAX: 107 GetWindowRect(hwnd, &rc); 108 ok_long(rc.right - rc.left, 0); 109 ok_long(rc.bottom - rc.top, 0); 110 ok_int(IsWindowVisible(hwnd), FALSE); 111 break; 112 case ACTION_NCCREATE: 113 GetWindowRect(hwnd, &rc); 114 ok_long(rc.right - rc.left, WIDTH); 115 ok_long(rc.bottom - rc.top, HEIGHT); 116 ok_int(IsWindowVisible(hwnd), FALSE); 117 break; 118 case ACTION_SHOW: 119 ShowWindow(hwnd, SW_SHOWNORMAL); 120 break; 121 case ACTION_IME_SETCONTEXT_OPEN: 122 ok(wParam == 1, "Step %d: wParam was %p\n", s_iStep, (void *)wParam); 123 ok(lParam == 0xC000000F, "Step %d: lParam was %p\n", s_iStep, (void *)lParam); 124 break; 125 case ACTION_IME_NOTIFY_OPEN: 126 ok(wParam == 2, "Step %d: wParam was %p\n", s_iStep, (void *)wParam); 127 ok(lParam == 0, "Step %d: lParam was %p\n", s_iStep, (void *)lParam); 128 break; 129 case ACTION_DESTROY: 130 DestroyWindow(hwnd); 131 break; 132 case ACTION_IME_SETCONTEXT_CLOSE: 133 ok(wParam == 0, "Step %d: wParam was %p\n", s_iStep, (void *)wParam); 134 ok(lParam == 0xC000000F, "Step %d: lParam was %p\n", s_iStep, (void *)lParam); 135 break; 136 case ACTION_IME_NOTIFY_CLOSE: 137 ok(wParam == 1, "Step %d: wParam was %p\n", s_iStep, (void *)wParam); 138 ok(lParam == 0, "Step %d: lParam was %p\n", s_iStep, (void *)lParam); 139 break; 140 case ACTION_HIDE: 141 ShowWindow(hwnd, SW_HIDE); 142 break; 143 case ACTION_DEACTIVATE: 144 SetForegroundWindow(GetDesktopWindow()); 145 break; 146 case ACTION_ACTIVATE: 147 SetForegroundWindow(hwnd); 148 break; 149 } 150 } 151 152 static void NextStage(HWND hwnd) 153 { 154 INT i, iAction; 155 const STAGE *pStage = &s_pStages[s_iStage]; 156 157 if (pStage->nType == STAGE_TYPE_COUNTING) 158 { 159 /* check counters */ 160 for (i = 0; i < pStage->nCount; ++i) 161 { 162 if (pStage->nCounters[i] > 0) 163 { 164 ok(pStage->nCounters[i] == s_nCounters[i], 165 "Line %d: s_nCounters[%d] expected %d but %d.\n", 166 pStage->nLine, i, pStage->nCounters[i], s_nCounters[i]); 167 } 168 } 169 } 170 171 /* go to next stage */ 172 ++s_iStage; 173 if (s_iStage >= s_cStages) 174 { 175 DestroyWindow(hwnd); 176 return; 177 } 178 trace("Stage %d (Line %d)\n", s_iStage, s_pStages[s_iStage].nLine); 179 180 s_iStep = 0; 181 ZeroMemory(s_nCounters, sizeof(s_nCounters)); 182 183 iAction = s_pStages[s_iStage].iFirstAction; 184 if (iAction) 185 PostMessage(hwnd, WM_COMMAND, iAction, 0); 186 } 187 188 static void DoStage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 189 { 190 INT i, iAction; 191 const STAGE *pStage; 192 s_bNextStage = FALSE; 193 194 if (s_iStage >= s_cStages) 195 return; 196 197 pStage = &s_pStages[s_iStage]; 198 switch (pStage->nType) 199 { 200 case STAGE_TYPE_SEQUENCE: 201 if (pStage->uMessages[s_iStep] == uMsg) 202 { 203 ok_int(1, 1); 204 ok(s_nLevel == pStage->nLevel, 205 "Line %d, Step %d: Level expected %d but %d.\n", 206 pStage->nLine, s_iStep, pStage->nLevel, s_nLevel); 207 ok(PARENT_MSG == pStage->uParentMsg, 208 "Line %d, Step %d: PARENT_MSG expected %u but %u.\n", 209 pStage->nLine, s_iStep, pStage->uParentMsg, PARENT_MSG); 210 211 iAction = pStage->iActions[s_iStep]; 212 if (iAction) 213 DoAction(hwnd, iAction, wParam, lParam); 214 215 ++s_iStep; 216 if (s_iStep >= pStage->nCount) 217 s_bNextStage = TRUE; 218 } 219 break; 220 case STAGE_TYPE_COUNTING: 221 for (i = 0; i < pStage->nCount; ++i) 222 { 223 if (pStage->uMessages[i] == uMsg) 224 { 225 ok_int(1, 1); 226 ok(s_nLevel == pStage->nLevel, 227 "Line %d: Level expected %d but %d.\n", 228 pStage->nLine, pStage->nLevel, s_nLevel); 229 ok(PARENT_MSG == pStage->uParentMsg, 230 "Line %d: PARENT_MSG expected %u but %u.\n", 231 pStage->nLine, pStage->uParentMsg, PARENT_MSG); 232 233 iAction = pStage->iActions[i]; 234 if (iAction) 235 DoAction(hwnd, iAction, wParam, lParam); 236 237 ++s_nCounters[i]; 238 239 if (i == pStage->nCount - 1) 240 s_bNextStage = TRUE; 241 break; 242 } 243 } 244 break; 245 } 246 247 if (s_bNextStage) 248 { 249 NextStage(hwnd); 250 } 251 } 252 253 static LRESULT CALLBACK 254 InnerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 255 { 256 switch (uMsg) 257 { 258 case WM_COMMAND: 259 DoAction(hwnd, LOWORD(wParam), 0, 0); 260 break; 261 case WM_TIMER: 262 KillTimer(hwnd, (UINT)wParam); 263 if (wParam == TIMEOUT_TIMER) 264 DestroyWindow(hwnd); 265 break; 266 case WM_DESTROY: 267 PostQuitMessage(0); 268 break; 269 case WM_NCCREATE: 270 SetTimer(hwnd, TIMEOUT_TIMER, TOTAL_TIMEOUT, NULL); 271 /* FALL THROUGH */ 272 default: 273 return DefWindowProc(hwnd, uMsg, wParam, lParam); 274 } 275 return 0; 276 } 277 278 static void DoBuildPrefix(void) 279 { 280 DWORD Flags = InSendMessageEx(NULL); 281 INT i = 0; 282 283 if (Flags & ISMEX_CALLBACK) 284 s_prefix[i++] = 'C'; 285 if (Flags & ISMEX_NOTIFY) 286 s_prefix[i++] = 'N'; 287 if (Flags & ISMEX_REPLIED) 288 s_prefix[i++] = 'R'; 289 if (Flags & ISMEX_SEND) 290 s_prefix[i++] = 'S'; 291 if (i == 0) 292 s_prefix[i++] = 'P'; 293 294 s_prefix[i++] = ':'; 295 s_prefix[i++] = ' '; 296 s_prefix[i] = 0; 297 } 298 299 static const STAGE s_GeneralStages[] = 300 { 301 /* Stage 0 */ 302 { 303 __LINE__, WM_NULL, 1, STAGE_TYPE_SEQUENCE, 0, 304 4, 305 { WM_GETMINMAXINFO, WM_NCCREATE, WM_NCCALCSIZE, WM_CREATE }, 306 { ACTION_FIRSTMINMAX, ACTION_NCCREATE, 0, 0 }, 307 }, 308 /* Stage 1 */ 309 { 310 __LINE__, WM_COMMAND, 2, STAGE_TYPE_SEQUENCE, ACTION_SHOW, 311 6, 312 { WM_SHOWWINDOW, WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGING, 313 WM_ACTIVATEAPP, WM_NCACTIVATE, WM_ACTIVATE }, 314 }, 315 { 316 __LINE__, WM_COMMAND, 2, STAGE_TYPE_SEQUENCE, ACTION_DESTROY, 317 6, 318 { WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGED, WM_NCACTIVATE, 319 WM_ACTIVATE, WM_ACTIVATEAPP, WM_KILLFOCUS }, 320 }, 321 { 322 __LINE__, WM_COMMAND, 2, STAGE_TYPE_SEQUENCE, 0, 323 2, 324 { WM_DESTROY, WM_NCDESTROY }, 325 }, 326 }; 327 328 static LRESULT CALLBACK 329 WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 330 { 331 LRESULT lResult; 332 333 /* Skip asynchronous WM_TIMER messages */ 334 if (uMsg == WM_TIMER) return InnerWindowProc(hwnd, uMsg, wParam, lParam); 335 336 /* build s_prefix */ 337 DoBuildPrefix(); 338 339 /* message dump */ 340 MD_msgdump(hwnd, uMsg, wParam, lParam); 341 342 ++s_nLevel; 343 s_msgStack[s_nLevel] = uMsg; 344 { 345 /* do inner task */ 346 DoStage(hwnd, uMsg, wParam, lParam); 347 lResult = InnerWindowProc(hwnd, uMsg, wParam, lParam); 348 } 349 --s_nLevel; 350 351 /* message return */ 352 StringCbCopyA(s_prefix, sizeof(s_prefix), "R: "); 353 MD_msgresult(hwnd, uMsg, wParam, lParam, lResult); 354 return lResult; 355 } 356 357 static void General_DoTest(void) 358 { 359 WNDCLASSA wc; 360 HWND hwnd; 361 MSG msg; 362 static const char s_szName[] = "MessageStateAnalyzerGeneral"; 363 364 trace("General_DoTest\n"); 365 DoInitialize(s_GeneralStages, ARRAYSIZE(s_GeneralStages)); 366 367 /* register window class */ 368 ZeroMemory(&wc, sizeof(wc)); 369 wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; 370 wc.lpfnWndProc = WindowProc; 371 wc.hInstance = GetModuleHandleA(NULL); 372 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 373 wc.hCursor = LoadCursor(NULL, IDC_ARROW); 374 wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); 375 wc.lpszClassName = s_szName; 376 if (!RegisterClassA(&wc)) 377 { 378 skip("RegisterClassW failed.\n"); 379 return; 380 } 381 382 /* create a window */ 383 hwnd = CreateWindowA(s_szName, s_szName, WS_OVERLAPPEDWINDOW, 384 0, 0, WIDTH, HEIGHT, NULL, NULL, 385 GetModuleHandleW(NULL), NULL); 386 if (!hwnd) 387 { 388 skip("CreateWindowW failed.\n"); 389 return; 390 } 391 392 /* message loop */ 393 while (GetMessageA(&msg, NULL, 0, 0)) 394 { 395 TranslateMessage(&msg); 396 DispatchMessageA(&msg); 397 } 398 399 ok_int(UnregisterClassA(s_szName, GetModuleHandleA(NULL)), TRUE); 400 401 DoFinish(); 402 } 403 404 static const STAGE s_IMEStages[] = 405 { 406 /* Stage 0 */ 407 { 408 __LINE__, WM_NULL, 1, STAGE_TYPE_SEQUENCE, 0, 409 4, 410 { WM_GETMINMAXINFO, WM_NCCREATE, WM_NCCALCSIZE, WM_CREATE }, 411 { ACTION_FIRSTMINMAX, ACTION_NCCREATE, 0, 0 }, 412 }, 413 /* Stage 1 */ 414 // show 415 { 416 __LINE__, WM_COMMAND, 2, STAGE_TYPE_SEQUENCE, ACTION_SHOW, 417 6, 418 { WM_SHOWWINDOW, WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGING, 419 WM_ACTIVATEAPP, WM_NCACTIVATE, WM_ACTIVATE }, 420 }, 421 { 422 __LINE__, WM_ACTIVATE, 3, STAGE_TYPE_SEQUENCE, 0, 423 1, 424 { WM_IME_SETCONTEXT }, 425 { ACTION_IME_SETCONTEXT_OPEN }, 426 }, 427 { 428 __LINE__, WM_IME_SETCONTEXT, 4, STAGE_TYPE_SEQUENCE, 0, 429 1, 430 { WM_IME_NOTIFY }, 431 { ACTION_IME_NOTIFY_OPEN }, 432 }, 433 // hide 434 { 435 __LINE__, WM_COMMAND, 2, STAGE_TYPE_SEQUENCE, ACTION_HIDE, 436 8, 437 { WM_SHOWWINDOW, WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGED, 438 WM_NCACTIVATE, WM_ACTIVATE, WM_ACTIVATEAPP, WM_KILLFOCUS, 439 WM_IME_SETCONTEXT }, 440 { 0, 0, 0, 0, 0, 0, 0, ACTION_IME_SETCONTEXT_CLOSE } 441 }, 442 { 443 __LINE__, WM_IME_SETCONTEXT, 3, STAGE_TYPE_SEQUENCE, 0, 444 1, 445 { WM_IME_NOTIFY }, 446 { ACTION_IME_NOTIFY_CLOSE } 447 }, 448 // show again 449 { 450 __LINE__, WM_COMMAND, 2, STAGE_TYPE_SEQUENCE, 3, 451 6, 452 { WM_SHOWWINDOW, WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGING, 453 WM_ACTIVATEAPP, WM_NCACTIVATE, WM_ACTIVATE }, 454 }, 455 { 456 __LINE__, WM_ACTIVATE, 3, STAGE_TYPE_SEQUENCE, 0, 457 1, 458 { WM_IME_SETCONTEXT }, 459 { ACTION_IME_SETCONTEXT_OPEN }, 460 }, 461 { 462 __LINE__, WM_IME_SETCONTEXT, 4, STAGE_TYPE_SEQUENCE, 0, 463 1, 464 { WM_IME_NOTIFY }, 465 { ACTION_IME_NOTIFY_OPEN }, 466 }, 467 // deactivate 468 { 469 __LINE__, WM_COMMAND, 2, STAGE_TYPE_SEQUENCE, ACTION_DEACTIVATE, 470 4, 471 { WM_NCACTIVATE, WM_ACTIVATE, WM_ACTIVATEAPP, WM_KILLFOCUS }, 472 }, 473 { 474 __LINE__, WM_COMMAND, 2, STAGE_TYPE_SEQUENCE, 0, 475 1, 476 { WM_IME_SETCONTEXT }, 477 { ACTION_IME_SETCONTEXT_CLOSE } 478 }, 479 { 480 __LINE__, WM_IME_SETCONTEXT, 3, STAGE_TYPE_SEQUENCE, 0, 481 1, 482 { WM_IME_NOTIFY }, 483 { ACTION_IME_NOTIFY_CLOSE } 484 }, 485 // activate 486 { 487 __LINE__, WM_ACTIVATE, 3, STAGE_TYPE_SEQUENCE, ACTION_ACTIVATE, 488 1, 489 { WM_IME_SETCONTEXT }, 490 { ACTION_IME_SETCONTEXT_OPEN } 491 }, 492 { 493 __LINE__, WM_IME_SETCONTEXT, 4, STAGE_TYPE_SEQUENCE, 0, 494 1, 495 { WM_IME_NOTIFY }, 496 { ACTION_IME_NOTIFY_OPEN }, 497 }, 498 // destroy 499 { 500 __LINE__, WM_COMMAND, 2, STAGE_TYPE_SEQUENCE, ACTION_DESTROY, 501 2, 502 { WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGED }, 503 }, 504 { 505 __LINE__, WM_COMMAND, 2, STAGE_TYPE_SEQUENCE, 0, 506 1, 507 { WM_IME_SETCONTEXT }, 508 { ACTION_IME_SETCONTEXT_CLOSE } 509 }, 510 { 511 __LINE__, WM_IME_SETCONTEXT, 3, STAGE_TYPE_SEQUENCE, 0, 512 1, 513 { WM_IME_NOTIFY }, 514 { ACTION_IME_NOTIFY_CLOSE } 515 }, 516 { 517 __LINE__, WM_COMMAND, 2, STAGE_TYPE_SEQUENCE, 0, 518 2, 519 { WM_DESTROY, WM_NCDESTROY }, 520 }, 521 }; 522 523 static void IME_DoTest(void) 524 { 525 WNDCLASSA wc; 526 HWND hwnd; 527 MSG msg; 528 static const char s_szName[] = "MessageStateAnalyzerIME"; 529 530 trace("IME_DoTest\n"); 531 DoInitialize(s_IMEStages, ARRAYSIZE(s_IMEStages)); 532 533 /* register window class */ 534 ZeroMemory(&wc, sizeof(wc)); 535 wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; 536 wc.lpfnWndProc = WindowProc; 537 wc.hInstance = GetModuleHandleA(NULL); 538 wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 539 wc.hCursor = LoadCursor(NULL, IDC_ARROW); 540 wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); 541 wc.lpszClassName = s_szName; 542 if (!RegisterClassA(&wc)) 543 { 544 skip("RegisterClassW failed.\n"); 545 return; 546 } 547 548 /* create a window */ 549 hwnd = CreateWindowA(s_szName, s_szName, WS_OVERLAPPEDWINDOW, 550 0, 0, WIDTH, HEIGHT, NULL, NULL, 551 GetModuleHandleW(NULL), NULL); 552 if (!hwnd) 553 { 554 skip("CreateWindowW failed.\n"); 555 return; 556 } 557 558 /* message loop */ 559 while (GetMessageA(&msg, NULL, 0, 0)) 560 { 561 TranslateMessage(&msg); 562 DispatchMessageA(&msg); 563 } 564 565 ok_int(UnregisterClassA(s_szName, GetModuleHandleA(NULL)), TRUE); 566 567 DoFinish(); 568 } 569 570 START_TEST(MessageStateAnalyzer) 571 { 572 General_DoTest(); 573 IME_DoTest(); 574 } 575