diff --git a/core/libs/prx/libSceHttp/CMakeLists.txt b/core/libs/prx/libSceHttp/CMakeLists.txt index 701f264762d9..ab630f375c88 100644 --- a/core/libs/prx/libSceHttp/CMakeLists.txt +++ b/core/libs/prx/libSceHttp/CMakeLists.txt @@ -8,6 +8,32 @@ if(BUILD_TESTING) target_link_libraries(guest_http_tests PRIVATE libSceHttp libc) configure_windows_unwind(guest_http_tests) add_test(NAME guest_http COMMAND guest_http_tests) + add_test(NAME guest_http_uri_locale COMMAND guest_http_tests --uri-locale) + set_tests_properties(guest_http_uri_locale PROPERTIES SKIP_RETURN_CODE 77) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(ANYPS5_HTTP_TEST_LOCALE_DIR "" CACHE PATH "Private locale directory for the HTTP URI regression") + set(http_test_locale_dir "${ANYPS5_HTTP_TEST_LOCALE_DIR}") + if(NOT http_test_locale_dir AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + set(private_locale_dir "${CMAKE_CURRENT_BINARY_DIR}/locales") + file(MAKE_DIRECTORY "${private_locale_dir}") + execute_process( + COMMAND localedef --no-archive -i en_US -f ISO-8859-1 "${private_locale_dir}/en_US.ISO8859-1" + RESULT_VARIABLE locale_result + OUTPUT_VARIABLE locale_output + ERROR_VARIABLE locale_error + TIMEOUT 30 + ) + if(locale_result STREQUAL "0") + set(http_test_locale_dir "${private_locale_dir}") + message(STATUS "HTTP URI locale: ${http_test_locale_dir}") + else() + message(STATUS "HTTP URI locale unavailable (${locale_result}): ${locale_output}${locale_error}") + endif() + endif() + if(http_test_locale_dir) + set_property(TEST guest_http_uri_locale APPEND PROPERTY ENVIRONMENT "LOCPATH=${http_test_locale_dir}") + endif() + endif() add_test_executable(guest_http_response_header_tests ${LIBS_INCLUDE_DIR}/tests/GuestHttpResponseHeader.cpp) target_include_directories(guest_http_response_header_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_http_response_header_tests PRIVATE libSceHttp libc) @@ -19,7 +45,7 @@ if(BUILD_TESTING) configure_windows_unwind(guest_http_options_tests) add_test(NAME guest_http_options COMMAND guest_http_options_tests) if(WIN32) - set_tests_properties(guest_http guest_http_response_header guest_http_options PROPERTIES ENVIRONMENT_MODIFICATION + set_tests_properties(guest_http guest_http_uri_locale guest_http_response_header guest_http_options PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() endif() diff --git a/core/libs/tests/GuestHttp.cpp b/core/libs/tests/GuestHttp.cpp index 661cd4f3cbdf..e1124192b553 100644 --- a/core/libs/tests/GuestHttp.cpp +++ b/core/libs/tests/GuestHttp.cpp @@ -5,9 +5,11 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include #include +#include #include #include #include +#include #include #include #include @@ -425,7 +427,36 @@ static void CheckCaList() { Require(sceHttpsFreeCaList(1, &list) == 0 && list.certs == nullptr && list.certsNum == 0); } -int main() { +static int CheckUriLocales() { + const std::string previousCtype = std::setlocale(LC_CTYPE, nullptr); + bool exercised = false; +#ifdef _WIN32 + const char* locales[] = {".1252"}; +#else + const char* locales[] = {"en_US.ISO8859-1", "en_US.iso88591", "en_US.UTF-8"}; +#endif + for (const char* locale : locales) { + if (!std::setlocale(LC_CTYPE, locale)) continue; + CheckUriEscapeBytes(); + CheckUriParseCharacters(); + const bool classifiesHighByte = std::isalpha(static_cast(0xE9)) != 0; + exercised |= classifiesHighByte; + std::printf("URI locale checks: %s; classifies 0xE9 as alphabetic: %s\n", locale, classifiesHighByte ? "yes" : "no"); + } + Require(std::setlocale(LC_CTYPE, previousCtype.c_str()) != nullptr); +#ifdef _WIN32 + Require(exercised); +#endif + if (!exercised) { + std::fputs("SKIP: URI locale regression requires a Latin-1 locale that classifies 0xE9 as alphabetic.\n", stderr); + return 77; + } + return 0; +} + +int main(int argc, char** argv) { + if (argc == 2 && std::strcmp(argv[1], "--uri-locale") == 0) return CheckUriLocales(); + Require(argc == 1); const std::uint64_t processSize = processParameters.size(); const std::uint32_t processMagic = 0x4942524f; std::uint32_t sdkVersion = 0x1000000; @@ -565,17 +596,6 @@ int main() { Require(std::setlocale(LC_CTYPE, "C") != nullptr); CheckUriEscapeBytes(); CheckUriParseCharacters(); -#ifdef _WIN32 - Require(std::setlocale(LC_CTYPE, ".1252") != nullptr); - CheckUriEscapeBytes(); - CheckUriParseCharacters(); -#else - for (const char* locale : {"en_US.ISO8859-1", "en_US.iso88591", "en_US.UTF-8"}) { - if (!std::setlocale(LC_CTYPE, locale)) continue; - CheckUriEscapeBytes(); - CheckUriParseCharacters(); - } -#endif Require(std::setlocale(LC_CTYPE, previousCtype.c_str()) != nullptr); constexpr const char* unescapeCases[][2] = { diff --git a/docs/dev/BUILD.md b/docs/dev/BUILD.md index 5c9b4ac5a46f..43e19da1fdb3 100644 --- a/docs/dev/BUILD.md +++ b/docs/dev/BUILD.md @@ -139,6 +139,10 @@ SDL and FreeType settings forced by the root `CMakeLists.txt` cannot be overridd With `ANYPS5_USE_SYSTEM_DEPS=ON` those libraries come from the system (on Debian and Ubuntu: `libsdl2-dev`, `libfreetype-dev`, `libvulkan-dev`, `spirv-headers`, `glslang-dev`, `glslang-tools` and `spirv-tools`). A missing package, Vulkan-Headers older than 1.4.310 (the agc driver uses `VK_KHR_maintenance8`), or an SDL2 without the HIDAPI joystick driver (sdl2-compat over SDL3, or a build with HIDAPI disabled, which breaks the pad and disk-audio tests), fails the configure with an error and never falls back to the submodule. The forced SDL and FreeType settings from the root `CMakeLists.txt` do not apply with the flag ON: the system SDL2 comes with whatever its distribution enabled. The prx libraries are shared objects, so SDL2 is linked from the system as a shared library; the default build keeps its own static SDL2. `ffmpeg-core`, `libjpeg-turbo`, `stb`, `VulkanMemoryAllocator`, `LibAtrac9`, `ogg` and `vorbis` always stay bundled and still need `git submodule update --init`. +## HTTP locale tests + +The Linux HTTP URI locale regression needs an ISO-8859-1 locale. With tests enabled, CMake uses host `localedef` and installed locale sources to generate a private `en_US.ISO8859-1` under the HTTP build directory. This runs from the checked-out PR tree, including when CI dispatches the workflow from `main`, and changes no system locales. `-DANYPS5_HTTP_TEST_LOCALE_DIR=` overrides generation. Only `guest_http_uri_locale` receives the private directory through `LOCPATH`. If `localedef` or its source data is unavailable, configuration reports it and leaves the test to probe installed locales. Without an effective Latin-1 locale the test explicitly skips; the CI skip audit rejects that loss of coverage. Windows requires `.1252`. + ## ThreadSanitizer A ThreadSanitizer build runs its tests with `TSAN_OPTIONS=suppressions=$PWD/tools/tsan-suppressions.txt`, which silences reports from Mesa's lavapipe, the LLVM it compiles shaders with, and PulseAudio: those system libraries are not instrumented, so ThreadSanitizer cannot see their locking. Guest memory written by the emulated GPU while a guest thread polls it, such as labels and fences, is shared without C++ atomics on purpose, as on the console, so ThreadSanitizer also reports those accesses.