Skip to content

Commit

Permalink
[SHELL32_APITEST] Add API tests for localized strings
Browse files Browse the repository at this point in the history
CORE-18893
  • Loading branch information
binarymaster committed Jul 10, 2024
1 parent 9f68f48 commit 2b611ae
Show file tree
Hide file tree
Showing 3 changed files with 295 additions and 2 deletions.
5 changes: 3 additions & 2 deletions modules/rostests/apitests/shell32/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ list(APPEND SOURCE
Int64ToString.cpp
IShellFolderViewCB.cpp
ItemIDList.cpp
LocaleTests.cpp
OpenAs_RunDLL.cpp
PathIsEqualOrSubFolder.cpp
PathIsTemporary.cpp
Expand Down Expand Up @@ -58,7 +59,7 @@ set_target_properties(shell32_apitest
ENABLE_EXPORTS TRUE
DEFINE_SYMBOL "")

target_link_libraries(shell32_apitest wine uuid ${PSEH_LIB} cpprt atl_classes)
target_link_libraries(shell32_apitest wine uuid ${PSEH_LIB} cpprt cppstl atl_classes)
set_module_type(shell32_apitest win32cui)
target_compile_definitions(shell32_apitest PRIVATE UNICODE _UNICODE)
add_importlibs(shell32_apitest user32 gdi32 shell32 shlwapi ole32 oleaut32 advapi32 shlwapi msvcrt kernel32 ntdll)
Expand All @@ -67,7 +68,7 @@ add_rostests_file(TARGET shell32_apitest)

# shell32_apitest_sub.exe
add_executable(shell32_apitest_sub shell32_apitest_sub.cpp)
target_link_libraries(shell32_apitest_sub cpprt atl_classes)
target_link_libraries(shell32_apitest_sub cpprt cppstl atl_classes)
set_module_type(shell32_apitest_sub win32gui UNICODE)
add_importlibs(shell32_apitest_sub msvcrt kernel32 user32 shell32 shlwapi ole32)
add_rostests_file(TARGET shell32_apitest_sub SUBDIR testdata)
290 changes: 290 additions & 0 deletions modules/rostests/apitests/shell32/LocaleTests.cpp
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();
}
2 changes: 2 additions & 0 deletions modules/rostests/apitests/shell32/testlist.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ extern void func_GUIDFromString(void);
extern void func_ILCreateFromPath(void);
extern void func_Int64ToString(void);
extern void func_IShellFolderViewCB(void);
extern void func_LocaleTests(void);
extern void func_menu(void);
extern void func_OpenAs_RunDLL(void);
extern void func_PathIsEqualOrSubFolder(void);
Expand Down Expand Up @@ -65,6 +66,7 @@ const struct test winetest_testlist[] =
{ "ILCreateFromPath", func_ILCreateFromPath },
{ "Int64ToString", func_Int64ToString },
{ "IShellFolderViewCB", func_IShellFolderViewCB },
{ "LocaleTests", func_LocaleTests },
{ "menu", func_menu },
{ "OpenAs_RunDLL", func_OpenAs_RunDLL },
{ "PathIsEqualOrSubFolder", func_PathIsEqualOrSubFolder },
Expand Down

0 comments on commit 2b611ae

Please sign in to comment.