ReactOS 0.4.17-dev-116-ga4b6fe9
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{
24};
25
27{
28 // s00.
35 // s06.
39 // s09.
42 // s11.
44};
45
46typedef struct PART_TEST
47{
53
54typedef struct PART
55{
59
60typedef struct PART_MATCH
61{
64
67std::set<LANGID> langs;
68std::map<E_MODULE, HMODULE> mod;
69std::map<E_STRING, PART_TEST> parts;
70
72{
75};
76
77static void InitParts(void)
78{
79 static const PART_PAIR s_pairs[] =
80 {
81 // { eString, { eModule, id, nParts } }
82 { SH32_PROGRAMS, { shell32, 45 /* IDS_PROGRAMS "Start Menu\Programs" */, 2 } },
83 { SH32_STARTUP, { shell32, 48 /* IDS_STARTUP "Start Menu\Programs\StartUp" */, 3 } },
84 { SH32_STARTMENU, { shell32, 51 /* IDS_STARTMENU "Start Menu" */, 1 } },
85 { SH32_PROGRAM_FILES, { shell32, 63 /* IDS_PROGRAM_FILES "Program Files" */, 1 } },
86 { SH32_PROGRAM_FILES_COMMON, { shell32, 65 /* IDS_PROGRAM_FILES_COMMON "Program Files\Common Files" */, 2 } },
87 { SH32_ADMINTOOLS, { shell32, 67 /* IDS_ADMINTOOLS "Start Menu\Programs\Administrative Tools" */, 3 } },
88 { UENV_STARTMENU, { userenv, 11 /* IDS_STARTMENU "Start Menu" */, 1 } },
89 { UENV_PROGRAMS, { userenv, 12 /* IDS_PROGRAMS "Start Menu\Programs" */, 2 } },
90 { UENV_STARTUP, { userenv, 13 /* IDS_STARTUP "Start Menu\Programs\StartUp" */, 3 } },
91 { SYSS_PROGRAMFILES, { syssetup, 3600 /* IDS_PROGRAMFILES "%SystemDrive%\Program Files" */, 2 } },
92 { SYSS_COMMONFILES, { syssetup, 3601 /* IDS_COMMONFILES "Common Files" */, 1 } },
93 { MMSY_STARTMENU, { mmsys, 5851 /* IDS_STARTMENU "Start Menu" */, 1 } },
94 };
95
96 for (auto& pair : s_pairs)
97 parts.insert(std::make_pair(pair.eString, pair.part_test));
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 // StartUp
116 { { SH32_STARTUP, 2 }, { UENV_STARTUP, 2 } },
117 // Program Files
119 { { SH32_PROGRAM_FILES, 0 }, { SYSS_PROGRAMFILES, 1 } },
120 // Common Files
122};
123
125{
127 MAKEINTRESOURCEW((uID >> 4) + 1), curLcid);
128
129 if (!hRes)
131 MAKEINTRESOURCEW((uID >> 4) + 1),
133
134 if (!hRes)
135 return 0;
136
137 HGLOBAL hMem = LoadResource(hInstance, hRes);
138 if (!hMem)
139 return 0;
140
141 PWCHAR p = (PWCHAR)LockResource(hMem);
142 for (UINT i = 0; i < (uID & 0x0F); i++) p += *p + 1;
143
144 int len = (*p > cchBufferMax ? cchBufferMax : *p);
145 memcpy(lpBuffer, p + 1, len * sizeof(WCHAR));
147 return len;
148}
149
151{
153 // Windows XP or lower: SetThreadLocale doesn't select user interface language
154 return GetLocalisedText(hInstance, uID, lpBuffer, cchBufferMax);
155 else
156 return LoadStringW(hInstance, uID, lpBuffer, cchBufferMax);
157}
158
160{
161 DWORD count = 0;
162 LPWSTR ptr = str;
163
164 if (*ptr == UNICODE_NULL)
165 return 0;
166
167 while ((ptr = wcschr(ptr, L'\\')))
168 {
169 count++;
170 ptr++;
171 }
172
173 return count + 1;
174}
175
177{
178 DWORD count = 0;
179 LPWSTR ptr = str, next;
180
181 while (count < num && (ptr = wcschr(ptr, L'\\')) != NULL)
182 {
183 count++;
184 ptr++;
185 }
186
187 if (!ptr)
188 ptr = str;
189
190 next = wcschr(ptr, L'\\');
191 *len = next ? next - ptr : wcslen(ptr);
192 return ptr;
193}
194
197{
198 langs.insert(lang);
199 return TRUE;
200}
201
203{
206 curLcid = lcid;
207}
208
209static void TEST_NumParts(void)
210{
211 for (auto& p : parts)
212 {
213 E_MODULE m = p.second.eModule;
214
215 if (!mod[m])
216 {
217 // Failure is already reported by TEST_LocaleTests().
218 continue;
219 }
220
221 WCHAR szBuffer[MAX_PATH];
222
223 LoadStringWrapW(mod[m], p.second.id, szBuffer, _countof(szBuffer));
224 p.second.gotParts = CountParts(szBuffer);
225
226 ok(p.second.gotParts == p.second.nParts,
227 "Locale 0x%04lX, num parts mismatch s%02d, expected %lu got %lu. Will skip related checks.\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 {
239 // Failure is already reported by TEST_LocaleTests().
240 return FALSE;
241 }
242
243 if (s.nParts != s.gotParts)
244 {
245 // Failure is already reported by TEST_NumParts().
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 !LoadPart(&match.p2, szP2, _countof(szP2)))
268 {
269 // Failures are already reported.
270 continue;
271 }
272
273 ok(wcscmp(szP1, szP2) == 0,
274 "Locale 0x%04lX, mismatching pairs s%02d:i%u s%02d:i%u, '%S'(l=%Iu) vs. '%S'(l=%Iu)\n",
275 curLcid, match.p1.Num, match.p1.Idx, match.p2.Num, match.p2.Idx, szP1, wcslen(szP1), szP2, wcslen(szP2));
276 }
277}
278
279static void TEST_LocaleTests(void)
280{
281 // Initialization
282 InitParts();
283
285 memset(&osvi, 0, sizeof(osvi));
286 osvi.dwOSVersionInfoSize = sizeof(osvi);
287
290
291 WCHAR szOldDir[MAX_PATH], szBuffer[MAX_PATH];
292 GetCurrentDirectoryW(_countof(szOldDir), szOldDir);
293
294 std::map<E_MODULE, LPCWSTR> lib;
295#define ADD_LIB(eModule, pszPath) lib.insert(std::make_pair(eModule, pszPath))
296
297 GetModuleFileNameW(NULL, szBuffer, _countof(szBuffer));
298 LPCWSTR pszFind = StrStrW(szBuffer, L"modules\\rostests\\unittests");
299 if (pszFind)
300 {
301 // We're running in ReactOS output folder
302 WCHAR szNewDir[MAX_PATH];
303
304 StringCchCopyNW(szNewDir, _countof(szNewDir), szBuffer, pszFind - szBuffer);
305 SetCurrentDirectoryW(szNewDir);
306
307 ADD_LIB(shell32, L"dll\\win32\\shell32\\shell32.dll");
308 ADD_LIB(userenv, L"dll\\win32\\userenv\\userenv.dll");
309 ADD_LIB(syssetup, L"dll\\win32\\syssetup\\syssetup.dll");
310 ADD_LIB(mmsys, L"dll\\cpl\\mmsys\\mmsys.cpl");
311 }
312 else
313 {
314 ADD_LIB(shell32, L"shell32.dll");
315 ADD_LIB(userenv, L"userenv.dll");
316 ADD_LIB(syssetup, L"syssetup.dll");
317 ADD_LIB(mmsys, L"mmsys.cpl");
318 }
319#undef ADD_LIB
320
321 for (auto& lb : lib)
322 {
323 E_MODULE m = lb.first;
324
326 if (!mod[m])
327 {
328 ok(FALSE, "Failed to load '%S', error %lu. Will skip related checks.\n", lib[m], GetLastError());
329 continue;
330 }
331
334 }
335
336 // Actual tests
337 for (auto& lang : langs)
338 {
340
343 }
344
345 // Perform cleanup
346 for (auto& m : mod)
347 {
348 if (!m.second)
349 continue;
350
351 FreeLibrary(m.second);
352 m.second = NULL;
353 }
354
355 SetCurrentDirectoryW(szOldDir);
356}
357
358START_TEST(LocaleTests)
359{
361}
static void TEST_NumParts(void)
E_MODULE
Definition: LocaleTests.cpp:19
@ mmsys
Definition: LocaleTests.cpp:23
@ shell32
Definition: LocaleTests.cpp:20
@ 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:67
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:66
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:27
@ UENV_STARTUP
Definition: LocaleTests.cpp:38
@ SH32_PROGRAM_FILES
Definition: LocaleTests.cpp:32
@ SYSS_PROGRAMFILES
Definition: LocaleTests.cpp:40
@ SH32_PROGRAM_FILES_COMMON
Definition: LocaleTests.cpp:33
@ UENV_PROGRAMS
Definition: LocaleTests.cpp:37
@ SYSS_COMMONFILES
Definition: LocaleTests.cpp:41
@ UENV_STARTMENU
Definition: LocaleTests.cpp:36
@ MMSY_STARTMENU
Definition: LocaleTests.cpp:43
@ 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:69
static void TEST_PartMatches(void)
std::map< E_MODULE, HMODULE > mod
Definition: LocaleTests.cpp:68
DWORD dwVersion
Definition: LocaleTests.cpp:65
static void InitParts(void)
Definition: LocaleTests.cpp:77
static LPWSTR GetPart(_In_ LPWSTR str, _In_ SIZE_T num, _Out_ SIZE_T *len)
static PART_MATCH PartMatches[]
#define ok(value,...)
Definition: atltest.h:57
#define START_TEST(x)
Definition: atltest.h:75
HINSTANCE hInstance
Definition: charmap.c:19
LPARAM lParam
Definition: combotst.c:139
#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 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:2168
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:2822
LCID lcid
Definition: locale.c:5656
_ACRTIMP size_t __cdecl wcslen(const wchar_t *)
Definition: wcs.c:2983
_ACRTIMP int __cdecl wcscmp(const wchar_t *, const wchar_t *)
Definition: wcs.c:1972
#define L(x)
Definition: resources.c:13
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
GLuint GLuint GLsizei count
Definition: gl.h:1545
GLuint GLuint GLsizei GLenum type
Definition: gl.h:1545
GLdouble s
Definition: gl.h:2039
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
USHORT LANGID
Definition: mui.h:9
LONG_PTR LPARAM
Definition: minwindef.h:175
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
static PVOID ptr
Definition: dispmode.c:27
unsigned int UINT
Definition: ndis.h:50
_Out_ LPWSTR lpBuffer
Definition: netsh.h:68
_In_ DWORD dwVersion
Definition: netsh.h:85
#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 RT_STRING
Definition: pedump.c:368
short WCHAR
Definition: pedump.c:58
_In_ UINT uID
Definition: shlwapi.h:156
static unsigned __int64 next
Definition: rand_nt.c:6
const WCHAR * str
#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 LoadStringW
Definition: utils.h:64
#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:74
E_STRING eString
Definition: LocaleTests.cpp:73
SIZE_T gotParts
Definition: LocaleTests.cpp:51
E_MODULE eModule
Definition: LocaleTests.cpp:48
SIZE_T nParts
Definition: LocaleTests.cpp:50
E_STRING Num
Definition: LocaleTests.cpp:56
UINT Idx
Definition: LocaleTests.cpp:57
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
const uint16_t * LPCWSTR
Definition: typedefs.h:57
uint16_t * LPWSTR
Definition: typedefs.h:56
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:338
#define LOCALE_ILANGUAGE
Definition: winnls.h:30
#define MAKEINTRESOURCEW(i)
Definition: winuser.h:582