ReactOS 0.4.16-dev-251-ga17b6e9
LocaleTests.cpp
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS interoperability tests
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Formal locale verification tests
5 * COPYRIGHT: Copyright 2024 Stanislav Motylkov <x86corez@gmail.com>
6 * Copyright 2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
7 */
8
9#include "interop.h"
10
11#include <winnls.h>
12#include <strsafe.h>
13#include <shlwapi.h>
14
15#include <set>
16#include <map>
17
19{
25};
26
28{
42};
43
44typedef struct PART_TEST
45{
51
52typedef struct PART
53{
57
58typedef struct PART_MATCH
59{
62
65std::set<LANGID> langs;
66std::map<E_MODULE, HMODULE> mod;
67std::map<E_STRING, PART_TEST> parts;
68
70{
73};
74
75static void InitParts(void)
76{
77 static const PART_PAIR s_pairs[] =
78 {
79 // { eString, { eModule, id, nParts } }
80 { SH32_PROGRAMS, { shell32, 45 /* IDS_PROGRAMS "Start Menu\Programs" */, 2 } },
81 { SH32_STARTUP, { shell32, 48 /* IDS_STARTUP "Start Menu\Programs\StartUp" */, 3 } },
82 { SH32_STARTMENU, { shell32, 51 /* IDS_STARTMENU "Start Menu" */, 1 } },
83 { SH32_PROGRAM_FILES, { shell32, 63 /* IDS_PROGRAM_FILES "Program Files" */, 1 } },
84 { SH32_PROGRAM_FILES_COMMON, { shell32, 65 /* IDS_PROGRAM_FILES_COMMON "Program Files\Common Files" */, 2 } },
85 { SH32_ADMINTOOLS, { shell32, 67 /* IDS_ADMINTOOLS "Start Menu\Programs\Administrative Tools" */, 3 } },
86 { UENV_STARTMENU, { userenv, 11 /* IDS_STARTMENU "Start Menu" */, 1 } },
87 { UENV_PROGRAMS, { userenv, 12 /* IDS_PROGRAMS "Start Menu\Programs" */, 2 } },
88 { UENV_STARTUP, { userenv, 13 /* IDS_STARTUP "Start Menu\Programs\StartUp" */, 3 } },
89 { SYSS_PROGRAMFILES, { syssetup, 3600 /* IDS_PROGRAMFILES "%SystemDrive%\Program Files" */, 2 } },
90 { SYSS_COMMONFILES, { syssetup, 3601 /* IDS_COMMONFILES "Common Files" */, 1 } },
91 { MMSY_STARTMENU, { mmsys, 5851 /* IDS_STARTMENU "Start Menu" */, 1 } },
92 { EOLD_PROGRAMS, { explorer_old, 10 /* IDS_PROGRAMS "Programs" */, 1 } },
93 };
94 for (auto& pair : s_pairs)
95 {
96 parts.insert(std::make_pair(pair.eString, pair.part_test));
97 }
98}
99
101{
102 // Start Menu
103 { { SH32_PROGRAMS, 0 }, { SH32_STARTUP, 0 } },
104 { { SH32_PROGRAMS, 0 }, { SH32_STARTMENU, 0 } },
105 { { SH32_PROGRAMS, 0 }, { SH32_ADMINTOOLS, 0 } },
106 { { SH32_PROGRAMS, 0 }, { UENV_STARTMENU, 0 } },
107 { { SH32_PROGRAMS, 0 }, { UENV_PROGRAMS, 0 } },
108 { { SH32_PROGRAMS, 0 }, { UENV_STARTUP, 0 } },
109 { { SH32_PROGRAMS, 0 }, { MMSY_STARTMENU, 0 } },
110 // Programs
111 { { SH32_PROGRAMS, 1 }, { SH32_STARTUP, 1 } },
112 { { SH32_PROGRAMS, 1 }, { SH32_ADMINTOOLS, 1 } },
113 { { SH32_PROGRAMS, 1 }, { UENV_PROGRAMS, 1 } },
114 { { SH32_PROGRAMS, 1 }, { UENV_STARTUP, 1 } },
115 { { SH32_PROGRAMS, 1 }, { EOLD_PROGRAMS, 0 } },
116 // StartUp
117 { { SH32_STARTUP, 2 }, { UENV_STARTUP, 2 } },
118 // Program Files
120 { { SH32_PROGRAM_FILES, 0 }, { SYSS_PROGRAMFILES, 1 } },
121 // Common Files
123};
124
126{
128 MAKEINTRESOURCEW((uID >> 4) + 1), curLcid);
129
130 if (!hRes)
132 MAKEINTRESOURCEW((uID >> 4) + 1),
134
135 if (!hRes)
136 return 0;
137
138 HGLOBAL hMem = LoadResource(hInstance, hRes);
139 if (!hMem)
140 return 0;
141
142 PWCHAR p = (PWCHAR)LockResource(hMem);
143 for (UINT i = 0; i < (uID & 0x0F); i++) p += *p + 1;
144
145 int len = (*p > cchBufferMax ? cchBufferMax : *p);
146 memcpy(lpBuffer, p + 1, len * sizeof(WCHAR));
148 return len;
149}
150
152{
154 // Windows XP or lower: SetThreadLocale doesn't select user interface language
155 return GetLocalisedText(hInstance, uID, lpBuffer, cchBufferMax);
156 else
157 return LoadStringW(hInstance, uID, lpBuffer, cchBufferMax);
158}
159
161{
162 DWORD count = 0;
163 LPWSTR ptr = str;
164
165 if (*ptr == UNICODE_NULL)
166 return 0;
167
168 while ((ptr = wcschr(ptr, L'\\')))
169 {
170 count++;
171 ptr++;
172 }
173
174 return count + 1;
175}
176
178{
179 DWORD count = 0;
180 LPWSTR ptr = str, next;
181
182 while (count < num && (ptr = wcschr(ptr, L'\\')) != NULL)
183 {
184 count++;
185 ptr++;
186 }
187
188 if (!ptr)
189 ptr = str;
190
191 next = wcschr(ptr, L'\\');
192 *len = next ? next - ptr : wcslen(ptr);
193 return ptr;
194}
195
198{
199 langs.insert(lang);
200 return TRUE;
201}
202
203static void SetLocale(_In_ LCID lcid)
204{
205 SetThreadLocale(lcid);
207 curLcid = lcid;
208}
209
210static void TEST_NumParts(void)
211{
212 for (auto& p : parts)
213 {
214 E_MODULE m = p.second.eModule;
215
216 if (!mod[m])
217 {
218 skip("No module for test %d\n", p.first);
219 continue;
220 }
221
222 WCHAR szBuffer[MAX_PATH];
223
224 LoadStringWrapW(mod[m], p.second.id, szBuffer, _countof(szBuffer));
225 p.second.gotParts = CountParts(szBuffer);
226
227 ok(p.second.nParts == p.second.gotParts, "Locale 0x%lX: Num parts mismatch %d - expected %lu, got %lu\n",
228 curLcid, p.first, p.second.nParts, p.second.gotParts);
229 }
230}
231
233{
234 auto s = parts[p->Num];
235 E_MODULE m = s.eModule;
236
237 if (!mod[m])
238 {
240 return FALSE;
241 }
242
243 if (s.nParts != s.gotParts)
244 {
246 return FALSE;
247 }
248
249 WCHAR szBuffer[MAX_PATH];
250 LPWSTR szPart;
251 SIZE_T len;
252
253 LoadStringWrapW(mod[m], s.id, szBuffer, _countof(szBuffer));
254 szPart = GetPart(szBuffer, p->Idx, &len);
255 StringCchCopyNW(str, size, szPart, len);
256
257 return TRUE;
258}
259
260static void TEST_PartMatches(void)
261{
262 for (auto& match : PartMatches)
263 {
264 WCHAR szP1[MAX_PATH], szP2[MAX_PATH];
265
266 if (!LoadPart(&match.p1, szP1, _countof(szP1)))
267 {
268 skip("%s for match test %d (pair 1)\n", GetLastError() == ERROR_FILE_NOT_FOUND
269 ? "No module" : "Invalid data", match.p1.Num);
270 continue;
271 }
272
273 if (!LoadPart(&match.p2, szP2, _countof(szP2)))
274 {
275 skip("%s for match test %d (pair 2)\n", GetLastError() == ERROR_FILE_NOT_FOUND
276 ? "No module" : "Invalid data", match.p2.Num);
277 continue;
278 }
279
280 ok(wcscmp(szP1, szP2) == 0, "Locale 0x%lX: Mismatching pairs %u:%u / %u:%u '%S' vs. '%S'\n",
281 curLcid, match.p1.Num, match.p1.Idx, match.p2.Num, match.p2.Idx, szP1, szP2);
282 }
283}
284
285static void TEST_LocaleTests(void)
286{
287 // Initialization
288 InitParts();
289
291 memset(&osvi, 0, sizeof(osvi));
292 osvi.dwOSVersionInfoSize = sizeof(osvi);
293
296
297 WCHAR szOldDir[MAX_PATH], szBuffer[MAX_PATH];
298 GetCurrentDirectoryW(_countof(szOldDir), szOldDir);
299
300 std::map<E_MODULE, LPCWSTR> lib;
301#define ADD_LIB(eModule, pszPath) lib.insert(std::make_pair(eModule, pszPath))
302
303 GetModuleFileNameW(NULL, szBuffer, _countof(szBuffer));
304 LPCWSTR pszFind = StrStrW(szBuffer, L"modules\\rostests\\unittests");
305 if (pszFind)
306 {
307 // We're running in ReactOS output folder
308 WCHAR szNewDir[MAX_PATH];
309
310 StringCchCopyNW(szNewDir, _countof(szNewDir), szBuffer, pszFind - szBuffer);
311 SetCurrentDirectoryW(szNewDir);
312
313 ADD_LIB(shell32, L"dll\\win32\\shell32\\shell32.dll");
314 ADD_LIB(userenv, L"dll\\win32\\userenv\\userenv.dll");
315 ADD_LIB(syssetup, L"dll\\win32\\syssetup\\syssetup.dll");
316 ADD_LIB(mmsys, L"dll\\cpl\\mmsys\\mmsys.cpl");
317 ADD_LIB(explorer_old, L"modules\\rosapps\\applications\\explorer-old\\explorer_old.exe");
318 }
319 else
320 {
321 ADD_LIB(shell32, L"shell32.dll");
322 ADD_LIB(userenv, L"userenv.dll");
323 ADD_LIB(syssetup, L"syssetup.dll");
324 ADD_LIB(mmsys, L"mmsys.cpl");
325 ADD_LIB(explorer_old, L"explorer_old.exe");
326 }
327#undef ADD_LIB
328
329 for (auto& lb : lib)
330 {
331 E_MODULE m = lb.first;
332
334 if (!mod[m])
335 {
336 trace("Failed to load '%S', error %lu\n", lib[m], GetLastError());
337 continue;
338 }
339
342 }
343
344 // Actual tests
345 for (auto& lang : langs)
346 {
348
351 }
352
353 // Perform cleanup
354 for (auto& m : mod)
355 {
356 if (!m.second)
357 continue;
358
359 FreeLibrary(m.second);
360 m.second = NULL;
361 }
362
363 SetCurrentDirectoryW(szOldDir);
364}
365
366START_TEST(LocaleTests)
367{
369}
static void TEST_NumParts(void)
E_MODULE
Definition: LocaleTests.cpp:19
@ mmsys
Definition: LocaleTests.cpp:23
@ shell32
Definition: LocaleTests.cpp:20
@ explorer_old
Definition: LocaleTests.cpp:24
@ userenv
Definition: LocaleTests.cpp:21
@ syssetup
Definition: LocaleTests.cpp:22
#define ADD_LIB(eModule, pszPath)
static void SetLocale(_In_ LCID lcid)
std::set< LANGID > langs
Definition: LocaleTests.cpp:65
static DWORD CountParts(_In_ LPWSTR str)
static int GetLocalisedText(_In_opt_ HINSTANCE hInstance, _In_ UINT uID, _Out_ LPWSTR lpBuffer, _In_ int cchBufferMax)
static BOOL CALLBACK find_locale_id_callback(_In_ HMODULE hModule, _In_ LPCWSTR type, _In_ LPCWSTR name, _In_ LANGID lang, _In_ LPARAM lParam)
static void TEST_LocaleTests(void)
LCID curLcid
Definition: LocaleTests.cpp:64
static int LoadStringWrapW(_In_opt_ HINSTANCE hInstance, _In_ UINT uID, _Out_ LPWSTR lpBuffer, _In_ int cchBufferMax)
static BOOL LoadPart(_In_ PART *p, _Out_ LPWSTR str, _In_ SIZE_T size)
E_STRING
Definition: LocaleTests.cpp:28
@ UENV_STARTUP
Definition: LocaleTests.cpp:37
@ EOLD_PROGRAMS
Definition: LocaleTests.cpp:41
@ SH32_PROGRAM_FILES
Definition: LocaleTests.cpp:32
@ SYSS_PROGRAMFILES
Definition: LocaleTests.cpp:38
@ SH32_PROGRAM_FILES_COMMON
Definition: LocaleTests.cpp:33
@ UENV_PROGRAMS
Definition: LocaleTests.cpp:36
@ SYSS_COMMONFILES
Definition: LocaleTests.cpp:39
@ UENV_STARTMENU
Definition: LocaleTests.cpp:35
@ MMSY_STARTMENU
Definition: LocaleTests.cpp:40
@ SH32_STARTUP
Definition: LocaleTests.cpp:30
@ SH32_STARTMENU
Definition: LocaleTests.cpp:31
@ SH32_ADMINTOOLS
Definition: LocaleTests.cpp:34
@ SH32_PROGRAMS
Definition: LocaleTests.cpp:29
std::map< E_STRING, PART_TEST > parts
Definition: LocaleTests.cpp:67
static void TEST_PartMatches(void)
std::map< E_MODULE, HMODULE > mod
Definition: LocaleTests.cpp:66
DWORD dwVersion
Definition: LocaleTests.cpp:63
static void InitParts(void)
Definition: LocaleTests.cpp:75
static LPWSTR GetPart(_In_ LPWSTR str, _In_ SIZE_T num, _Out_ SIZE_T *len)
static PART_MATCH PartMatches[]
#define trace
Definition: atltest.h:70
#define ok(value,...)
Definition: atltest.h:57
#define skip(...)
Definition: atltest.h:64
#define START_TEST(x)
Definition: atltest.h:75
HINSTANCE hInstance
Definition: charmap.c:19
LPARAM lParam
Definition: combotst.c:139
static TAGREF LPCWSTR LPDWORD LPVOID lpBuffer
Definition: db.cpp:175
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
HMODULE hModule
Definition: animate.c:44
LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
Definition: string.c:590
#define wcschr
Definition: compat.h:17
#define GetCurrentDirectoryW(x, y)
Definition: compat.h:756
#define SetLastError(x)
Definition: compat.h:752
#define FreeLibrary(x)
Definition: compat.h:748
#define MAX_PATH
Definition: compat.h:34
#define CALLBACK
Definition: compat.h:35
DWORD WINAPI GetModuleFileNameW(HINSTANCE hModule, LPWSTR lpFilename, DWORD nSize)
Definition: loader.c:600
HINSTANCE WINAPI DECLSPEC_HOTPATCH LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
Definition: loader.c:288
BOOL WINAPI SetCurrentDirectoryW(IN LPCWSTR lpPathName)
Definition: path.c:2249
LANGID WINAPI SetThreadUILanguage(IN LANGID LangId)
Definition: thread.c:937
BOOL WINAPI GetVersionExW(IN LPOSVERSIONINFOW lpVersionInformation)
Definition: version.c:37
LPVOID WINAPI LockResource(HGLOBAL handle)
Definition: res.c:550
HRSRC WINAPI FindResourceExW(HMODULE hModule, LPCWSTR type, LPCWSTR name, WORD lang)
Definition: res.c:164
HGLOBAL WINAPI LoadResource(HINSTANCE hModule, HRSRC hRsrc)
Definition: res.c:532
BOOL WINAPI EnumResourceLanguagesW(HMODULE hmod, LPCWSTR type, LPCWSTR name, ENUMRESLANGPROCW lpfun, LONG_PTR lparam)
Definition: res.c:480
BOOL WINAPI SetThreadLocale(LCID lcid)
Definition: locale.c:2820
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
GLuint GLuint GLsizei GLenum type
Definition: gl.h:1545
GLdouble s
Definition: gl.h:2039
GLuint GLuint GLsizei count
Definition: gl.h:1545
GLsizeiptr size
Definition: glext.h:5919
GLfloat GLfloat p
Definition: glext.h:8902
GLuint GLuint num
Definition: glext.h:9618
GLenum GLsizei len
Definition: glext.h:6722
const GLfloat * m
Definition: glext.h:10848
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
_CRTIMP size_t __cdecl wcslen(_In_z_ const wchar_t *_Str)
USHORT LANGID
Definition: mui.h:9
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
#define ERROR_FILE_NOT_FOUND
Definition: disk.h:79
static PVOID ptr
Definition: dispmode.c:27
unsigned int UINT
Definition: ndis.h:50
#define _Out_
Definition: no_sal2.h:160
#define _In_
Definition: no_sal2.h:158
#define _In_opt_
Definition: no_sal2.h:212
#define UNICODE_NULL
#define SORT_DEFAULT
#define MAKELCID(lgid, srtid)
#define L(x)
Definition: ntvdm.h:50
#define RT_STRING
Definition: pedump.c:368
static unsigned __int64 next
Definition: rand_nt.c:6
const WCHAR * str
_Check_return_ _CRTIMP int __cdecl wcscmp(_In_z_ const wchar_t *_Str1, _In_z_ const wchar_t *_Str2)
#define MAKELANGID(p, s)
Definition: nls.h:15
#define LANG_ENGLISH
Definition: nls.h:52
#define SUBLANG_DEFAULT
Definition: nls.h:168
DWORD LCID
Definition: nls.h:13
#define memset(x, y, z)
Definition: compat.h:39
#define _WIN32_WINNT_WS03
Definition: sdkddkver.h:23
#define _countof(array)
Definition: sndvol32.h:70
STRSAFEAPI StringCchCopyNW(STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszSrc, size_t cchToCopy)
Definition: strsafe.h:236
PART_TEST part_test
Definition: LocaleTests.cpp:72
E_STRING eString
Definition: LocaleTests.cpp:71
SIZE_T gotParts
Definition: LocaleTests.cpp:49
E_MODULE eModule
Definition: LocaleTests.cpp:46
SIZE_T nParts
Definition: LocaleTests.cpp:48
E_STRING Num
Definition: LocaleTests.cpp:54
UINT Idx
Definition: LocaleTests.cpp:55
ULONG dwOSVersionInfoSize
Definition: rtltypes.h:237
ULONG dwMajorVersion
Definition: rtltypes.h:238
ULONG dwMinorVersion
Definition: rtltypes.h:239
Definition: match.c:28
Definition: name.c:39
Definition: _pair.h:47
ULONG_PTR SIZE_T
Definition: typedefs.h:80
uint16_t * PWCHAR
Definition: typedefs.h:56
OSVERSIONINFO osvi
Definition: ver.c:28
static const WCHAR lang[]
Definition: wbemdisp.c:287
DWORD WINAPI GetLastError(void)
Definition: except.c:1042
#define LOAD_LIBRARY_AS_DATAFILE
Definition: winbase.h:368
LONG_PTR LPARAM
Definition: windef.h:208
#define ERROR_INVALID_DATA
Definition: winerror.h:116
#define LOCALE_ILANGUAGE
Definition: winnls.h:25
int WINAPI LoadStringW(_In_opt_ HINSTANCE hInstance, _In_ UINT uID, _Out_writes_to_(cchBufferMax, return+1) LPWSTR lpBuffer, _In_ int cchBufferMax)
#define MAKEINTRESOURCEW(i)
Definition: winuser.h:582
__wchar_t WCHAR
Definition: xmlstorage.h:180
WCHAR * LPWSTR
Definition: xmlstorage.h:184
const WCHAR * LPCWSTR
Definition: xmlstorage.h:185