forked from reactos/reactos
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SHELL32_APITEST] Add API tests for localized strings
CORE-18893
- Loading branch information
1 parent
9f68f48
commit 2b611ae
Showing
3 changed files
with
295 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,290 @@ | ||
/* | ||
* PROJECT: ReactOS API tests | ||
* LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+) | ||
* PURPOSE: Formal locale verification tests | ||
* COPYRIGHT: Copyright 2024 Stanislav Motylkov <[email protected]> | ||
*/ | ||
|
||
#include "shelltest.h" | ||
|
||
#include <winnls.h> | ||
#include <strsafe.h> | ||
|
||
#include <set> | ||
#include <map> | ||
|
||
enum E_MODULE | ||
{ | ||
shell32, | ||
userenv, | ||
syssetup, | ||
mmsys, | ||
explorer_old, | ||
}; | ||
|
||
enum E_STRING | ||
{ | ||
SH32_PROGRAMS, | ||
SH32_STARTUP, | ||
SH32_STARTMENU, | ||
SH32_PROGRAM_FILES, | ||
SH32_PROGRAM_FILES_COMMON, | ||
SH32_ADMINTOOLS, | ||
UENV_STARTMENU, | ||
UENV_PROGRAMS, | ||
UENV_STARTUP, | ||
SYSS_PROGRAMFILES, | ||
SYSS_COMMONFILES, | ||
MMSY_STARTMENU, | ||
EOLD_PROGRAMS, | ||
}; | ||
|
||
typedef struct PART_TEST | ||
{ | ||
E_MODULE eModule; | ||
UINT id; | ||
SIZE_T nParts; | ||
} PART_TEST; | ||
|
||
typedef struct PART | ||
{ | ||
E_STRING Num; | ||
UINT Idx; | ||
} PART; | ||
|
||
typedef struct PART_MATCH | ||
{ | ||
PART p1, p2; | ||
} PART_MATCH; | ||
|
||
LCID curLcid = 0; | ||
std::set<LANGID> langs; | ||
std::map<E_MODULE, HMODULE> mod; | ||
std::map<E_STRING, PART_TEST> parts = | ||
{ | ||
{ SH32_PROGRAMS, { shell32, 45 /* IDS_PROGRAMS "Start Menu\Programs" */, 2 } }, | ||
{ SH32_STARTUP, { shell32, 48 /* IDS_STARTUP "Start Menu\Programs\StartUp" */, 3 } }, | ||
{ SH32_STARTMENU, { shell32, 51 /* IDS_STARTMENU "Start Menu" */, 1 } }, | ||
{ SH32_PROGRAM_FILES, { shell32, 63 /* IDS_PROGRAM_FILES "Program Files" */, 1 } }, | ||
{ SH32_PROGRAM_FILES_COMMON, { shell32, 65 /* IDS_PROGRAM_FILES_COMMON "Program Files\Common Files" */, 2 } }, | ||
{ SH32_ADMINTOOLS, { shell32, 67 /* IDS_ADMINTOOLS "Start Menu\Programs\Administrative Tools" */, 3 } }, | ||
{ UENV_STARTMENU, { userenv, 11 /* IDS_STARTMENU "Start Menu" */, 1 } }, | ||
{ UENV_PROGRAMS, { userenv, 12 /* IDS_PROGRAMS "Start Menu\Programs" */, 2 } }, | ||
{ UENV_STARTUP, { userenv, 13 /* IDS_STARTUP "Start Menu\Programs\StartUp" */, 3 } }, | ||
{ SYSS_PROGRAMFILES, { syssetup, 3600 /* IDS_PROGRAMFILES "%SystemDrive%\Program Files" */, 2 } }, | ||
{ SYSS_COMMONFILES, { syssetup, 3601 /* IDS_COMMONFILES "Common Files" */, 1 } }, | ||
{ MMSY_STARTMENU, { mmsys, 5851 /* IDS_STARTMENU "Start Menu" */, 1 } }, | ||
{ EOLD_PROGRAMS, { explorer_old, 10 /* IDS_PROGRAMS "Programs" */, 1 } }, | ||
}; | ||
|
||
static PART_MATCH PartMatches[] = | ||
{ | ||
// Start Menu | ||
{ { SH32_PROGRAMS, 0 }, { SH32_STARTUP, 0 } }, | ||
{ { SH32_PROGRAMS, 0 }, { SH32_STARTMENU, 0 } }, | ||
{ { SH32_PROGRAMS, 0 }, { SH32_ADMINTOOLS, 0 } }, | ||
{ { SH32_PROGRAMS, 0 }, { UENV_STARTMENU, 0 } }, | ||
{ { SH32_PROGRAMS, 0 }, { UENV_PROGRAMS, 0 } }, | ||
{ { SH32_PROGRAMS, 0 }, { UENV_STARTUP, 0 } }, | ||
{ { SH32_PROGRAMS, 0 }, { MMSY_STARTMENU, 0 } }, | ||
// Programs | ||
{ { SH32_PROGRAMS, 1 }, { SH32_STARTUP, 1 } }, | ||
{ { SH32_PROGRAMS, 1 }, { SH32_ADMINTOOLS, 1 } }, | ||
{ { SH32_PROGRAMS, 1 }, { UENV_PROGRAMS, 1 } }, | ||
{ { SH32_PROGRAMS, 1 }, { UENV_STARTUP, 1 } }, | ||
{ { SH32_PROGRAMS, 1 }, { EOLD_PROGRAMS, 0 } }, | ||
// StartUp | ||
{ { SH32_STARTUP, 2 }, { UENV_STARTUP, 2 } }, | ||
// Program Files | ||
{ { SH32_PROGRAM_FILES, 0 }, { SH32_PROGRAM_FILES_COMMON, 0 } }, | ||
{ { SH32_PROGRAM_FILES, 0 }, { SYSS_PROGRAMFILES, 1 } }, | ||
// Common Files | ||
{ { SH32_PROGRAM_FILES_COMMON, 1 }, { SYSS_COMMONFILES, 0 } }, | ||
}; | ||
|
||
static DWORD CountParts(_In_ LPWSTR str) | ||
{ | ||
DWORD count = 0; | ||
LPWSTR ptr = str; | ||
|
||
if (*ptr == UNICODE_NULL) | ||
return 0; | ||
|
||
while ((ptr = wcschr(ptr, L'\\')) != NULL) | ||
{ | ||
count++; | ||
ptr++; | ||
} | ||
|
||
return count + 1; | ||
} | ||
|
||
static LPWSTR GetPart(_In_ LPWSTR str, _In_ SIZE_T num, _Out_ SIZE_T* len) | ||
{ | ||
DWORD count = 0; | ||
LPWSTR ptr = str, next; | ||
|
||
while (count < num && (ptr = wcschr(ptr, L'\\')) != NULL) | ||
{ | ||
count++; | ||
ptr++; | ||
} | ||
|
||
next = wcschr(ptr, L'\\'); | ||
*len = next ? next - ptr : wcslen(ptr); | ||
return ptr; | ||
} | ||
|
||
static BOOL CALLBACK find_locale_id_callback( | ||
_In_ HMODULE hModule, _In_ LPCWSTR type, _In_ LPCWSTR name, _In_ LANGID lang, _In_ LPARAM lParam) | ||
{ | ||
langs.insert(lang); | ||
return TRUE; | ||
} | ||
|
||
static void SetLocale(_In_ LCID lcid) | ||
{ | ||
SetThreadLocale(lcid); | ||
SetThreadUILanguage(lcid); | ||
curLcid = lcid; | ||
} | ||
|
||
static void TEST_NumParts(void) | ||
{ | ||
for (auto i = parts.begin(); i != parts.end(); i++) | ||
{ | ||
E_MODULE m = i->second.eModule; | ||
|
||
if (!mod[m]) | ||
{ | ||
skip("No module for test #%d\n", i->first); | ||
continue; | ||
} | ||
|
||
WCHAR szBuffer[MAX_PATH]; | ||
|
||
LoadStringW(mod[m], i->second.id, szBuffer, _countof(szBuffer)); | ||
ok(i->second.nParts == CountParts(szBuffer), "Locale 0x%lX: Num parts mismatch #%d - expected %lu, got %lu\n", | ||
curLcid, i->first, i->second.nParts, CountParts(szBuffer)); | ||
} | ||
} | ||
|
||
static BOOL LoadPart(_In_ PART* p, _Out_ LPWSTR str, _In_ SIZE_T size) | ||
{ | ||
auto s = parts[p->Num]; | ||
E_MODULE m = s.eModule; | ||
|
||
if (!mod[m]) | ||
return FALSE; | ||
|
||
WCHAR szBuffer[MAX_PATH]; | ||
LPWSTR szPart; | ||
SIZE_T len; | ||
|
||
LoadStringW(mod[m], s.id, szBuffer, _countof(szBuffer)); | ||
szPart = GetPart(szBuffer, p->Idx, &len); | ||
StringCchCopyNW(str, size, szPart, len); | ||
|
||
return TRUE; | ||
} | ||
|
||
static void TEST_PartMatches(void) | ||
{ | ||
for (SIZE_T i = 0; i < _countof(PartMatches); i++) | ||
{ | ||
WCHAR szP1[MAX_PATH], szP2[MAX_PATH]; | ||
|
||
if (!LoadPart(&PartMatches[i].p1, szP1, _countof(szP1))) | ||
{ | ||
skip("No module for match test #%lu pair 1\n", i); | ||
continue; | ||
} | ||
|
||
if (!LoadPart(&PartMatches[i].p2, szP2, _countof(szP2))) | ||
{ | ||
skip("No module for match test #%lu pair 2\n", i); | ||
continue; | ||
} | ||
|
||
ok(wcscmp(szP1, szP2) == 0, "Locale 0x%lX: Mismatching pairs #%u:%u / #%u:%u '%S' vs. '%S'\n", | ||
curLcid, PartMatches[i].p1.Num, PartMatches[i].p1.Idx, PartMatches[i].p2.Num, PartMatches[i].p2.Idx, szP1, szP2); | ||
} | ||
} | ||
|
||
static void TEST_LocaleTests(void) | ||
{ | ||
// Initialization | ||
WCHAR szOldDir[MAX_PATH], szBuffer[MAX_PATH]; | ||
GetCurrentDirectoryW(_countof(szOldDir), szOldDir); | ||
|
||
std::map<E_MODULE, LPCWSTR> lib; | ||
|
||
GetModuleFileNameW(NULL, szBuffer, _countof(szBuffer)); | ||
LPWSTR pszFind = StrStrW(szBuffer, L"modules\\rostests\\apitests"); | ||
if (pszFind) | ||
{ | ||
WCHAR szNewDir[MAX_PATH]; | ||
|
||
StringCchCopyNW(szNewDir, _countof(szNewDir), szBuffer, pszFind - szBuffer); | ||
SetCurrentDirectoryW(szNewDir); | ||
|
||
lib = { | ||
{shell32, L"dll\\win32\\shell32\\shell32.dll"}, | ||
{userenv, L"dll\\win32\\userenv\\userenv.dll"}, | ||
{syssetup, L"dll\\win32\\syssetup\\syssetup.dll"}, | ||
{mmsys, L"dll\\cpl\\mmsys\\mmsys.cpl"}, | ||
{explorer_old, L"modules\\rosapps\\applications\\explorer-old\\explorer_old.exe"}, | ||
}; | ||
} | ||
else | ||
{ | ||
lib = { | ||
{shell32, L"shell32.dll"}, | ||
{userenv, L"userenv.dll"}, | ||
{syssetup, L"syssetup.dll"}, | ||
{mmsys, L"mmsys.cpl"}, | ||
{explorer_old, L"explorer_old.exe"}, | ||
}; | ||
} | ||
|
||
for (auto i = lib.begin(); i != lib.end(); i++) | ||
{ | ||
E_MODULE m = i->first; | ||
|
||
mod[m] = LoadLibraryExW(lib[m], NULL, LOAD_LIBRARY_AS_DATAFILE); | ||
if (!mod[m]) | ||
{ | ||
trace("Failed to load '%S', error %lu\n", lib[m], GetLastError()); | ||
continue; | ||
} | ||
|
||
EnumResourceLanguagesW(mod[m], (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE, | ||
find_locale_id_callback, NULL); | ||
} | ||
|
||
// Actual tests | ||
for (auto i = langs.begin(); i != langs.end(); i++) | ||
{ | ||
SetLocale(MAKELCID(*i, SORT_DEFAULT)); | ||
|
||
TEST_NumParts(); | ||
TEST_PartMatches(); | ||
} | ||
|
||
// Perform cleanup | ||
for (auto i = mod.begin(); i != mod.end(); i++) | ||
{ | ||
if (!i->second) | ||
continue; | ||
|
||
FreeLibrary(i->second); | ||
i->second = NULL; | ||
} | ||
|
||
SetCurrentDirectoryW(szOldDir); | ||
} | ||
|
||
START_TEST(LocaleTests) | ||
{ | ||
TEST_LocaleTests(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters