mirror of
https://github.com/reactos/reactos.git
synced 2024-11-02 04:37:32 +00:00
fb58782012
Add IME conversion tests to verify IMM/IME support. CORE-11700
285 lines
8 KiB
C
285 lines
8 KiB
C
/*
|
|
* PROJECT: ReactOS api tests
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: Test for Japanese IME conversion
|
|
* COPYRIGHT: Copyright 2022 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#include <imm.h>
|
|
#include <wine/test.h>
|
|
|
|
/*
|
|
* We emulate some keyboard typing on dialog box and watch the conversion of Japanese IME.
|
|
* This program needs Japanese environment and Japanese IME.
|
|
* Tested on Japanese WinXP and Japanese Win10.
|
|
*/
|
|
|
|
#define INTERVAL 300
|
|
#define WM_PRESS_KEY_COMPLETE (WM_USER + 100)
|
|
|
|
/* The test entry structure */
|
|
typedef struct tagTEST_ENTRY
|
|
{
|
|
const UINT *pKeys;
|
|
UINT cKeys;
|
|
const void *pvResult;
|
|
INT cWM_IME_ENDCOMPOSITION;
|
|
} TEST_ENTRY, *PTEST_ENTRY;
|
|
|
|
// The Japanese word "テスト" conversion in Romaji
|
|
static const UINT s_keys1[] =
|
|
{
|
|
'T', 'E', 'S', 'U', 'T', 'O', VK_SPACE, VK_RETURN
|
|
};
|
|
// The Japanese word "調査員" conversion in Romaji
|
|
static const UINT s_keys2[] =
|
|
{
|
|
'C', 'H', 'O', 'U', 'S', 'A', 'I', 'N', 'N', VK_SPACE, VK_RETURN
|
|
};
|
|
|
|
#ifdef UNICODE
|
|
#define AorW(a, w) w
|
|
#else
|
|
#define AorW(a, w) a
|
|
#endif
|
|
|
|
/* The test entries */
|
|
static const TEST_ENTRY s_entries[] =
|
|
{
|
|
// "テスト"
|
|
{ s_keys1, _countof(s_keys1), AorW("\x83\x65\x83\x58\x83\x67", L"\x30C6\x30B9\x30C8"), 1 },
|
|
// "調査員"
|
|
{ s_keys2, _countof(s_keys2), AorW("\x92\xB2\x8D\xB8\x88\xF5", L"\x8ABF\x67FB\x54E1"), 1 },
|
|
};
|
|
|
|
static INT s_iEntry = 0;
|
|
static INT s_cWM_IME_ENDCOMPOSITION = 0;
|
|
static WNDPROC s_fnOldEditWndProc = NULL;
|
|
|
|
#ifdef UNICODE
|
|
static LPSTR WideToAnsi(INT nCodePage, LPCWSTR pszWide)
|
|
{
|
|
static CHAR s_sz[512];
|
|
WideCharToMultiByte(nCodePage, 0, pszWide, -1, s_sz, _countof(s_sz), NULL, NULL);
|
|
return s_sz;
|
|
}
|
|
#endif
|
|
|
|
/* The window procedure for textbox to watch the IME conversion */
|
|
static LRESULT CALLBACK
|
|
EditWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_IME_ENDCOMPOSITION:
|
|
{
|
|
const TEST_ENTRY *entry = &s_entries[s_iEntry];
|
|
HIMC hIMC;
|
|
LONG cbResult, cbBuffer;
|
|
LPTSTR pszResult;
|
|
|
|
/* Check conversion results of composition string */
|
|
hIMC = ImmGetContext(hwnd);
|
|
cbResult = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0);
|
|
trace("cbResult: %ld\n", cbResult);
|
|
if (cbResult > 0) /* Ignore zero string */
|
|
{
|
|
ok(hIMC != NULL, "hIMC was NULL\n");
|
|
++s_cWM_IME_ENDCOMPOSITION;
|
|
|
|
cbBuffer = cbResult + sizeof(WCHAR);
|
|
pszResult = (LPTSTR)calloc(cbBuffer, sizeof(BYTE)); /* Zero-fill */
|
|
ok(pszResult != NULL, "pszResult was NULL\n");
|
|
ImmGetCompositionString(hIMC, GCS_RESULTSTR, pszResult, cbBuffer);
|
|
#ifdef UNICODE
|
|
trace("%s\n", WideToAnsi(CP_ACP, (LPTSTR)pszResult));
|
|
#else
|
|
trace("%s\n", (LPTSTR)pszResult);
|
|
#endif
|
|
ok(lstrcmp(pszResult, (LPTSTR)entry->pvResult) == 0, "pszResult differs\n");
|
|
free(pszResult);
|
|
}
|
|
|
|
ImmReleaseContext(hwnd, hIMC);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return CallWindowProc(s_fnOldEditWndProc, hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
/* Timer IDs */
|
|
#define STAGE_1 10001
|
|
#define STAGE_2 10002
|
|
#define STAGE_3 10003
|
|
#define STAGE_4 10004
|
|
#define STAGE_5 10005
|
|
|
|
/* WM_INITDIALOG */
|
|
static BOOL OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
|
|
{
|
|
/* Subclass the textbox to watch the IME conversion */
|
|
HWND hEdt1 = GetDlgItem(hwnd, edt1);
|
|
s_fnOldEditWndProc = (WNDPROC)SetWindowLongPtr(hEdt1, GWLP_WNDPROC, (LONG_PTR)EditWindowProc);
|
|
|
|
/* Go to first stage */
|
|
SetTimer(hwnd, STAGE_1, INTERVAL, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
/* WM_COMMAND */
|
|
static void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
switch (id)
|
|
{
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Emulate keyboard typing */
|
|
static VOID PressKey(UINT vk)
|
|
{
|
|
INPUT inputs[2];
|
|
ZeroMemory(inputs, sizeof(inputs));
|
|
inputs[0].type = INPUT_KEYBOARD;
|
|
inputs[0].ki.wVk = vk;
|
|
inputs[1].type = INPUT_KEYBOARD;
|
|
inputs[1].ki.wVk = vk;
|
|
inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;
|
|
SendInput(_countof(inputs), inputs, sizeof(INPUT));
|
|
}
|
|
|
|
/* WM_TIMER */
|
|
static void OnTimer(HWND hwnd, UINT id)
|
|
{
|
|
HIMC hIMC;
|
|
INT i;
|
|
const TEST_ENTRY *entry = &s_entries[s_iEntry];
|
|
static DWORD dwOldConversion, dwOldSentence;
|
|
|
|
KillTimer(hwnd, id);
|
|
|
|
switch (id)
|
|
{
|
|
case STAGE_1:
|
|
/* Check focus. See WM_INITDIALOG return code. */
|
|
ok(GetFocus() == GetDlgItem(hwnd, edt1), "GetFocus() was %p\n", GetFocus());
|
|
|
|
hIMC = ImmGetContext(hwnd);
|
|
ok(hIMC != NULL, "hIMC was NULL");
|
|
if (hIMC)
|
|
{
|
|
/* Open the IME */
|
|
ImmSetOpenStatus(hIMC, TRUE);
|
|
/* Save the IME conversion status */
|
|
ImmGetConversionStatus(hIMC, &dwOldConversion, &dwOldSentence);
|
|
/* Modify the IME conversion status */
|
|
ImmSetConversionStatus(hIMC,
|
|
IME_CMODE_FULLSHAPE | IME_CMODE_ROMAN | IME_CMODE_NATIVE,
|
|
IME_SMODE_SINGLECONVERT);
|
|
|
|
ImmReleaseContext(hwnd, hIMC);
|
|
}
|
|
/* Initialize the counter */
|
|
s_cWM_IME_ENDCOMPOSITION = 0;
|
|
/* Go to next stage */
|
|
SetTimer(hwnd, STAGE_2, INTERVAL, NULL);
|
|
break;
|
|
|
|
case STAGE_2:
|
|
/* Emulate keyboard typing */
|
|
for (i = 0; i < entry->cKeys; ++i)
|
|
{
|
|
PressKey(entry->pKeys[i]);
|
|
}
|
|
/* Wait for message queue processed */
|
|
PostMessage(hwnd, WM_PRESS_KEY_COMPLETE, 0, 0);
|
|
break;
|
|
|
|
case STAGE_3:
|
|
/* Revert the IME conversion status */
|
|
hIMC = ImmGetContext(hwnd);
|
|
ok(hIMC != NULL, "hIMC was NULL");
|
|
if (hIMC)
|
|
{
|
|
ImmSetConversionStatus(hIMC, dwOldConversion, dwOldSentence);
|
|
ImmReleaseContext(hwnd, hIMC);
|
|
}
|
|
/* Go to next stage */
|
|
SetTimer(hwnd, STAGE_4, INTERVAL, NULL);
|
|
break;
|
|
|
|
case STAGE_4:
|
|
/* Check the counter */
|
|
ok_int(s_cWM_IME_ENDCOMPOSITION, entry->cWM_IME_ENDCOMPOSITION);
|
|
if (s_cWM_IME_ENDCOMPOSITION < entry->cWM_IME_ENDCOMPOSITION)
|
|
{
|
|
skip("Some tests were skipped.\n");
|
|
}
|
|
|
|
/* Go to next test entry */
|
|
++s_iEntry;
|
|
if (s_iEntry == _countof(s_entries))
|
|
PostMessage(hwnd, WM_CLOSE, 0, 0); /* No more entry */
|
|
else
|
|
SetTimer(hwnd, STAGE_1, INTERVAL, NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Dialog procedure */
|
|
static INT_PTR CALLBACK
|
|
DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(hwnd, WM_INITDIALOG, OnInitDialog);
|
|
HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
|
|
HANDLE_MSG(hwnd, WM_TIMER, OnTimer);
|
|
|
|
case WM_PRESS_KEY_COMPLETE:
|
|
/* Message queue is processed. Go to next stage. */
|
|
SetTimer(hwnd, STAGE_3, INTERVAL, NULL);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
START_TEST(JapanImeConvTestW)
|
|
#else
|
|
START_TEST(JapanImeConvTestA)
|
|
#endif
|
|
{
|
|
/* Is the system Japanese? */
|
|
if (PRIMARYLANGID(GetSystemDefaultLangID()) != LANG_JAPANESE)
|
|
{
|
|
skip("This testcase is for Japanese only.\n");
|
|
return;
|
|
}
|
|
|
|
/* Is IMM enabled? */
|
|
if (!GetSystemMetrics(SM_IMMENABLED))
|
|
{
|
|
skip("SM_IMMENABLED is OFF.\n");
|
|
return;
|
|
}
|
|
|
|
/* Check the current keyboard layout is IME */
|
|
if (!ImmIsIME(GetKeyboardLayout(0)))
|
|
{
|
|
skip("The IME keyboard layout was not default\n");
|
|
return;
|
|
}
|
|
|
|
DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(1), NULL, DialogProc);
|
|
|
|
if (s_iEntry < _countof(s_entries))
|
|
skip("Some tests were skipped.\n");
|
|
}
|