ReactOS 0.4.16-dev-2613-g9533ad7
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{
41};
42
43typedef struct PART_TEST
44{
50
51typedef struct PART
52{
56
57typedef struct PART_MATCH
58{
61
64std::set<LANGID> langs;
65std::map<E_MODULE, HMODULE> mod;
66std::map<E_STRING, PART_TEST> parts;
67
69{
72};
73
74static void InitParts(void)
75{
76 static const PART_PAIR s_pairs[] =
77 {
78 // { eString, { eModule, id, nParts } }
79 { SH32_PROGRAMS, { shell32, 45 /* IDS_PROGRAMS "Start Menu\Programs" */, 2 } },
80 { SH32_STARTUP, { shell32, 48 /* IDS_STARTUP "Start Menu\Programs\StartUp" */, 3 } },
81 { SH32_STARTMENU, { shell32, 51 /* IDS_STARTMENU "Start Menu" */, 1 } },
82 { SH32_PROGRAM_FILES, { shell32, 63 /* IDS_PROGRAM_FILES "Program Files" */, 1 } },
83 { SH32_PROGRAM_FILES_COMMON, { shell32, 65 /* IDS_PROGRAM_FILES_COMMON "Program Files\Common Files" */, 2 } },
84 { SH32_ADMINTOOLS, { shell32, 67 /* IDS_ADMINTOOLS "Start Menu\Programs\Administrative Tools" */, 3 } },
85 { UENV_STARTMENU, { userenv, 11 /* IDS_STARTMENU "Start Menu" */, 1 } },
86 { UENV_PROGRAMS, { userenv, 12 /* IDS_PROGRAMS "Start Menu\Programs" */, 2 } },
87 { UENV_STARTUP, { userenv, 13 /* IDS_STARTUP "Start Menu\Programs\StartUp" */, 3 } },
88 { SYSS_PROGRAMFILES, { syssetup, 3600 /* IDS_PROGRAMFILES "%SystemDrive%\Program Files" */, 2 } },
89 { SYSS_COMMONFILES, { syssetup, 3601 /* IDS_COMMONFILES "Common Files" */, 1 } },
90 { MMSY_STARTMENU, { mmsys, 5851 /* IDS_STARTMENU "Start Menu" */, 1 } },
91 };
92 for (auto& pair : s_pairs)
93 {
94 parts.insert(std::make_pair(pair.eString, pair.part_test));
95 }
96}
97
99{
100 // Start Menu
101 { { SH32_PROGRAMS, 0 }, { SH32_STARTUP, 0 } },
102 { { SH32_PROGRAMS, 0 }, { SH32_STARTMENU, 0 } },
103 { { SH32_PROGRAMS, 0 }, { SH32_ADMINTOOLS, 0 } },
104 { { SH32_PROGRAMS, 0 }, { UENV_STARTMENU, 0 } },
105 { { SH32_PROGRAMS, 0 }, { UENV_PROGRAMS, 0 } },
106 { { SH32_PROGRAMS, 0 }, { UENV_STARTUP, 0 } },
107 { { SH32_PROGRAMS, 0 }, { MMSY_STARTMENU, 0 } },
108 // Programs
109 { { SH32_PROGRAMS, 1 }, { SH32_STARTUP, 1 } },
110 { { SH32_PROGRAMS, 1 }, { SH32_ADMINTOOLS, 1 } },
111 { { SH32_PROGRAMS, 1 }, { UENV_PROGRAMS, 1 } },
112 { { SH32_PROGRAMS, 1 }, { UENV_STARTUP, 1 } },
113 { { SH32_PROGRAMS, 1 }, { EOLD_PROGRAMS, 0 } },
114 // StartUp
115 { { SH32_STARTUP, 2 }, { UENV_STARTUP, 2 } },
116 // Program Files
118 { { SH32_PROGRAM_FILES, 0 }, { SYSS_PROGRAMFILES, 1 } },
119 // Common Files
121};
122
124{
126 MAKEINTRESOURCEW((uID >> 4) + 1), curLcid);
127
128 if (!hRes)
130 MAKEINTRESOURCEW((uID >> 4) + 1),
132
133 if (!hRes)
134 return 0;
135
136 HGLOBAL hMem = LoadResource(hInstance, hRes);
137 if (!hMem)
138 return 0;
139
140 PWCHAR p = (PWCHAR)LockResource(hMem);
141 for (UINT i = 0; i < (uID & 0x0F); i++) p += *p + 1;
142
143 int len = (*p > cchBufferMax ? cchBufferMax : *p);
144 memcpy(lpBuffer, p + 1, len * sizeof(WCHAR));
146 return len;
147}
148
150{
152 // Windows XP or lower: SetThreadLocale doesn't select user interface language
153 return GetLocalisedText(hInstance, uID, lpBuffer, cchBufferMax);
154 else
155 return LoadStringW(hInstance, uID, lpBuffer, cchBufferMax);
156}
157
159{
160 DWORD count = 0;
161 LPWSTR ptr = str;
162
163 if (*ptr == UNICODE_NULL)
164 return 0;
165
166 while ((ptr = wcschr(ptr, L'\\')))
167 {
168 count++;
169 ptr++;
170 }
171
172 return count + 1;
173}
174
176{
177 DWORD count = 0;
178 LPWSTR ptr = str, next;
179
180 while (count < num && (ptr = wcschr(ptr, L'\\')) != NULL)
181 {
182 count++;
183 ptr++;
184 }
185
186 if (!ptr)
187 ptr = str;
188
189 next = wcschr(ptr, L'\\');
190 *len = next ? next - ptr : wcslen(ptr);
191 return ptr;
192}
193
196{
197 langs.insert(lang);
198 return TRUE;
199}
200
202{
205 curLcid = lcid;
206}
207
208static void TEST_NumParts(void)
209{
210 for (auto& p : parts)
211 {
212 E_MODULE m = p.second.eModule;
213
214 if (!mod[m])
215 {
216 skip("No module for test %d\n", p.first);
217 continue;
218 }
219
220 WCHAR szBuffer[MAX_PATH];
221
222 LoadStringWrapW(mod[m], p.second.id, szBuffer, _countof(szBuffer));
223 p.second.gotParts = CountParts(szBuffer);
224
225 ok(p.second.nParts == p.second.gotParts, "Locale 0x%lX: Num parts mismatch %d - expected %lu, got %lu\n",
226 curLcid, p.first, p.second.nParts, p.second.gotParts);
227 }
228}
229
231{
232 auto s = parts[p->Num];
233 E_MODULE m = s.eModule;
234
235 if (!mod[m])
236 {
238 return FALSE;
239 }
240
241 if (s.nParts != s.gotParts)
242 {
244 return FALSE;
245 }
246
247 WCHAR szBuffer[MAX_PATH];
248 LPWSTR szPart;
249 SIZE_T len;
250
251 LoadStringWrapW(mod[m], s.id, szBuffer, _countof(szBuffer));
252 szPart = GetPart(szBuffer, p->Idx, &len);
253 StringCchCopyNW(str, size, szPart, len);
254
255 return TRUE;
256}
257
258static void TEST_PartMatches(void)
259{
260 for (auto& match : PartMatches)
261 {
262 WCHAR szP1[MAX_PATH], szP2[MAX_PATH];
263
264 if (!LoadPart(&match.p1, szP1, _countof(szP1)))
265 {
266 skip("%s for match test %d (pair 1)\n", GetLastError() == ERROR_FILE_NOT_FOUND
267 ? "No module" : "Invalid data", match.p1.Num);
268 continue;
269 }
270
271 if (!LoadPart(&match.p2, szP2, _countof(szP2)))
272 {
273 skip("%s for match test %d (pair 2)\n", GetLastError() == ERROR_FILE_NOT_FOUND
274 ? "No module" : "Invalid data", match.p2.Num);
275 continue;
276 }
277
278 ok(wcscmp(szP1, szP2) == 0, "Locale 0x%lX: Mismatching pairs %u:%u / %u:%u '%S' vs. '%S'\n",
279 curLcid, match.p1.Num, match.p1.Idx, match.p2.Num, match.p2.Idx, szP1, szP2);
280 }
281}
282
283static void TEST_LocaleTests(void)
284{
285 // Initialization
286 InitParts();
287
289 memset(&osvi, 0, sizeof(osvi));
290 osvi.dwOSVersionInfoSize = sizeof(osvi);
291
294
295 WCHAR szOldDir[MAX_PATH], szBuffer[MAX_PATH];
296 GetCurrentDirectoryW(_countof(szOldDir), szOldDir);
297
298 std::map<E_MODULE, LPCWSTR> lib;
299#define ADD_LIB(eModule, pszPath) lib.insert(std::make_pair(eModule, pszPath))
300
301 GetModuleFileNameW(NULL, szBuffer, _countof(szBuffer));
302 LPCWSTR pszFind = StrStrW(szBuffer, L"modules\\rostests\\unittests");
303 if (pszFind)
304 {
305 // We're running in ReactOS output folder
306 WCHAR szNewDir[MAX_PATH];
307
308 StringCchCopyNW(szNewDir, _countof(szNewDir), szBuffer, pszFind - szBuffer);
309 SetCurrentDirectoryW(szNewDir);
310
311 ADD_LIB(shell32, L"dll\\win32\\shell32\\shell32.dll");
312 ADD_LIB(userenv, L"dll\\win32\\userenv\\userenv.dll");
313 ADD_LIB(syssetup, L"dll\\win32\\syssetup\\syssetup.dll");
314 ADD_LIB(mmsys, L"dll\\cpl\\mmsys\\mmsys.cpl");
315 }
316 else
317 {
318 ADD_LIB(shell32, L"shell32.dll");
319 ADD_LIB(userenv, L"userenv.dll");
320 ADD_LIB(syssetup, L"syssetup.dll");
321 ADD_LIB(mmsys, L"mmsys.cpl");
322 }
323#undef ADD_LIB
324
325 for (auto& lb : lib)
326 {
327 E_MODULE m = lb.first;
328
330 if (!mod[m])
331 {
332 trace("Failed to load '%S', error %lu\n", lib[m], GetLastError());
333 continue;
334 }
335
338 }
339
340 // Actual tests
341 for (auto& lang : langs)
342 {
344
347 }
348
349 // Perform cleanup
350 for (auto& m : mod)
351 {
352 if (!m.second)
353 continue;
354
355 FreeLibrary(m.second);
356 m.second = NULL;
357 }
358
359 SetCurrentDirectoryW(szOldDir);
360}
361
362START_TEST(LocaleTests)
363{
365}
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:64
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:63
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:36
@ EOLD_PROGRAMS
Definition: LocaleTests.cpp:40
@ SH32_PROGRAM_FILES
Definition: LocaleTests.cpp:31
@ SYSS_PROGRAMFILES
Definition: LocaleTests.cpp:37
@ SH32_PROGRAM_FILES_COMMON
Definition: LocaleTests.cpp:32
@ UENV_PROGRAMS
Definition: LocaleTests.cpp:35
@ SYSS_COMMONFILES
Definition: LocaleTests.cpp:38
@ UENV_STARTMENU
Definition: LocaleTests.cpp:34
@ MMSY_STARTMENU
Definition: LocaleTests.cpp:39
@ SH32_STARTUP
Definition: LocaleTests.cpp:29
@ SH32_STARTMENU
Definition: LocaleTests.cpp:30
@ SH32_ADMINTOOLS
Definition: LocaleTests.cpp:33
@ SH32_PROGRAMS
Definition: LocaleTests.cpp:28
std::map< E_STRING, PART_TEST > parts
Definition: LocaleTests.cpp:66
static void TEST_PartMatches(void)
std::map< E_MODULE, HMODULE > mod
Definition: LocaleTests.cpp:65
DWORD dwVersion
Definition: LocaleTests.cpp:62
static void InitParts(void)
Definition: LocaleTests.cpp:74
static LPWSTR GetPart(_In_ LPWSTR str, _In_ SIZE_T num, _Out_ SIZE_T *len)
static PART_MATCH PartMatches[]
Definition: LocaleTests.cpp:98
#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
#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: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
#define ERROR_FILE_NOT_FOUND
Definition: disk.h:79
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:71
E_STRING eString
Definition: LocaleTests.cpp:70
SIZE_T gotParts
Definition: LocaleTests.cpp:48
E_MODULE eModule
Definition: LocaleTests.cpp:45
SIZE_T nParts
Definition: LocaleTests.cpp:47
E_STRING Num
Definition: LocaleTests.cpp:53
UINT Idx
Definition: LocaleTests.cpp:54
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 ERROR_INVALID_DATA
Definition: winerror.h:238
#define LOCALE_ILANGUAGE
Definition: winnls.h:30
#define MAKEINTRESOURCEW(i)
Definition: winuser.h:582