Files
AnyPS5/core/libs/tests/GuestSystemServiceDateTimeFormat.cpp
T
MarcZX10 08ecf40df1 test(libSceSystemService): run ANYPS5_DATE_FORMAT and ANYPS5_TIME_FORMAT through sceSystemServiceParamGetInt
One process checks that unset and empty values keep 1 and 1, that 0 to 2 and 0
to 1 are read at each query, that every near miss is refused for the variable
it was set on and not the other, and that the time zone, summer time and
language parameters and the null-pointer checks are unaffected. Twelve more
processes run auto with the host variables set one way each (nothing set, C,
POSIX, an uninstalled locale), mixed with explicit and invalid values, and
count the stderr notices exactly: one per variable set to auto, none otherwise,
and none again when the host changes after the first answer.
2026-11-07 02:50:57 +00:00

171 lines
6.6 KiB
C++

#include "prx/libSceSystemService/include/SystemService.hpp"
#include "SceTypes.hpp"
#include "prx/libSceSystemService/SystemService.hpp"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iterator>
#include <stdexcept>
#include <string>
#ifdef __linux__
#include <unistd.h>
#endif
namespace {
void Require(bool value, const char* what) {
if (value) return;
std::fprintf(stdout, "check failed: %s\n", what);
std::abort();
}
void Set(const char* name, const char* value) {
#ifdef _WIN32
Require(_putenv_s(name, value != nullptr ? value : "") == 0, "environment change");
#else
Require((value != nullptr ? ::setenv(name, value, 1) : ::unsetenv(name)) == 0, "environment change");
#endif
}
#ifdef __linux__
const char* Argument(const char* text) { return std::strcmp(text, "-") == 0 ? nullptr : text; }
#endif
constexpr int Throws = -100;
int Query(int paramId, const char* variable) {
int value = -12345;
try {
Require(sceSystemServiceParamGetInt(paramId, &value) == SYSTEM_SERVICE_OK, "parameter answers");
} catch (const std::runtime_error& error) {
const std::string text = error.what();
Require(value == -12345, "a refused value leaves the output untouched");
Require(text.find(variable) != std::string::npos, "the error names the variable");
Require(text.find("or auto, not") != std::string::npos, "the error says auto is accepted");
return Throws;
}
return value;
}
int DateFormat() { return Query(SYSTEM_SERVICE_PARAM_ID_DATE_FORMAT, "ANYPS5_DATE_FORMAT"); }
int TimeFormat() { return Query(SYSTEM_SERVICE_PARAM_ID_TIME_FORMAT, "ANYPS5_TIME_FORMAT"); }
void SetBoth(const char* date, const char* time) {
Set("ANYPS5_DATE_FORMAT", date);
Set("ANYPS5_TIME_FORMAT", time);
}
void RequireExplicitValues() {
SetBoth(nullptr, nullptr);
Require(DateFormat() == 1 && TimeFormat() == 1, "unset keeps day-month-year and 24-hour");
SetBoth("", "");
Require(DateFormat() == 1 && TimeFormat() == 1, "empty keeps day-month-year and 24-hour");
for (int date = 0; date <= 2; ++date) {
const std::string text = std::to_string(date);
SetBoth(text.c_str(), nullptr);
Require(DateFormat() == date, "explicit date format");
Require(TimeFormat() == 1, "the time format ignores the date variable");
}
for (int time = 0; time <= 1; ++time) {
const std::string text = std::to_string(time);
SetBoth(nullptr, text.c_str());
Require(TimeFormat() == time, "explicit time format");
Require(DateFormat() == 1, "the date format ignores the time variable");
}
SetBoth("2", "0");
Require(DateFormat() == 2 && TimeFormat() == 0, "both explicit");
SetBoth("0", "1");
Require(DateFormat() == 0 && TimeFormat() == 1, "both explicit again, read at each query");
}
void RequireRefusedValues() {
for (const char* text : {"3", "-1", "+1", " 1", "1 ", "1x", "0x1", "auto ", "autoo", "-auto", "yes"}) {
SetBoth(text, nullptr);
Require(DateFormat() == Throws, "an invalid date format is refused");
Require(TimeFormat() == 1, "an invalid date format does not affect the time format");
SetBoth(nullptr, text);
Require(DateFormat() == 1, "an invalid time format does not affect the date format");
Require(TimeFormat() == Throws, "an invalid time format is refused");
}
SetBoth("2", "2");
Require(DateFormat() == 2, "2 is a date format");
Require(TimeFormat() == Throws, "2 is not a time format");
SetBoth(nullptr, nullptr);
}
void RequireNeighbours() {
SetBoth("2", "0");
int zone = -12345;
Require(sceSystemServiceParamGetInt(SYSTEM_SERVICE_PARAM_ID_TIME_ZONE, &zone) == SYSTEM_SERVICE_OK && zone != -12345, "the time zone still answers");
int summer = -12345;
Require(sceSystemServiceParamGetInt(SYSTEM_SERVICE_PARAM_ID_SUMMERTIME, &summer) == SYSTEM_SERVICE_OK && (summer == 0 || summer == 1), "summer time still answers");
int language = -12345;
Require(sceSystemServiceParamGetInt(SYSTEM_SERVICE_PARAM_ID_LANG, &language) == SYSTEM_SERVICE_OK, "the language still answers");
Require(sceSystemServiceParamGetInt(SYSTEM_SERVICE_PARAM_ID_DATE_FORMAT, nullptr) == SYSTEM_SERVICE_ERROR_PARAMETER, "null date format pointer");
Require(sceSystemServiceParamGetInt(SYSTEM_SERVICE_PARAM_ID_TIME_FORMAT, nullptr) == SYSTEM_SERVICE_ERROR_PARAMETER, "null time format pointer");
SetBoth(nullptr, nullptr);
}
#ifdef __linux__
int Notices(const std::string& path) {
std::ifstream stream(path);
const std::string text((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
int count = 0;
for (std::size_t at = text.find("=auto: "); at != std::string::npos; at = text.find("=auto: ", at + 1)) ++count;
return count;
}
int ParseExpected(const char* text) { return std::strcmp(text, "throws") == 0 ? Throws : std::atoi(text); }
int RunHostCase(char** argument) {
char path[] = "/tmp/anyps5_date_time_format_XXXXXX";
const int file = ::mkstemp(path);
Require(file >= 0, "temporary file");
std::fflush(stderr);
Require(::dup2(file, 2) >= 0, "redirect stderr");
SetBoth(Argument(argument[1]), Argument(argument[2]));
Set("LC_ALL", Argument(argument[3]));
Set("LC_TIME", Argument(argument[4]));
Set("LANG", Argument(argument[5]));
const int expectedDate = ParseExpected(argument[6]);
const int expectedTime = ParseExpected(argument[7]);
const int expectedNotices = std::atoi(argument[8]);
Require(DateFormat() == expectedDate, "date format of the host case");
Require(TimeFormat() == expectedTime, "time format of the host case");
Require(DateFormat() == expectedDate && TimeFormat() == expectedTime, "second query agrees");
Set("LC_ALL", nullptr);
Set("LC_TIME", nullptr);
Set("LANG", "en_US.UTF-8");
Require(DateFormat() == expectedDate && TimeFormat() == expectedTime, "a later change of the host does not change the answer");
std::fflush(stderr);
const int notices = Notices(path);
::unlink(path);
::close(file);
if (notices != expectedNotices) {
std::fprintf(stdout, "expected %d notices, found %d\n", expectedNotices, notices);
return 1;
}
return 0;
}
#endif
}
int main(int argc, char** argv) {
if (argc == 1) {
RequireExplicitValues();
RequireRefusedValues();
RequireNeighbours();
return 0;
}
#ifdef __linux__
Require(argc == 9, "arguments: date time LC_ALL LC_TIME LANG expectedDate expectedTime notices");
return RunHostCase(argv);
#else
static_cast<void>(argv);
return 2;
#endif
}