locale/test/test_winapi_formatting.cpp
Alexander Grund 211734c2c8 Add test for formatting large (u)int64 numbers
As reported in #235 formatting the first number which doesn't fit into
int64_t anymore fails to add the thousands separators.
I.e.:
`9223372036854775807` -> `9,223,372,036,854,775,807`
`9223372036854775808` -> `9223372036854775808`

Add a test reproducing that that for all backends.
2025-01-15 13:57:44 +01:00

187 lines
6.0 KiB
C++

//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/locale/encoding.hpp>
#include <boost/locale/formatting.hpp>
#include <boost/locale/generator.hpp>
#include <boost/locale/info.hpp>
#include <boost/locale/localization_backend.hpp>
#include <boost/core/ignore_unused.hpp>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <utility>
#ifndef BOOST_LOCALE_NO_WINAPI_BACKEND
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <windows.h>
#endif
#include "../src/win32/lcid.hpp"
#include "boostLocale/test/tools.hpp"
#include "boostLocale/test/unit_test.hpp"
#include "formatting_common.hpp"
template<typename CharType>
void test_by_char(const std::locale& l, std::string name, int lcid)
{
typedef std::basic_stringstream<CharType> ss_type;
using namespace boost::locale;
{
std::cout << "--- Testing as::posix" << std::endl;
ss_type ss;
ss.imbue(l);
TEST(ss << 1045.45);
double n;
TEST(ss >> n);
TEST_EQ(n, 1045.45);
TEST_EQ(to_utf8(ss.str()), "1045.45");
}
{
std::cout << "--- Testing as::number" << std::endl;
ss_type ss;
ss.imbue(l);
ss << as::number;
TEST(ss << 1045.45);
double n;
TEST(ss >> n);
TEST_EQ(n, 1045.45);
if(name == "ru_RU.UTF-8") {
BOOST_LOCALE_START_CONST_CONDITION
if(sizeof(CharType) == 1) // Space
TEST_EQ(to_utf8(ss.str()), "1 045,45");
else // Non-breaking space
TEST_EQ(to_utf8(ss.str()),
"1\xC2\xA0"
"045,45");
BOOST_LOCALE_END_CONST_CONDITION
} else
TEST_EQ(to_utf8(ss.str()), "1,045.45");
}
{
std::cout << "--- Testing as::currency " << std::endl;
ss_type ss;
ss.imbue(l);
ss << as::currency;
TEST(ss << 1043.34);
#ifndef BOOST_LOCALE_NO_WINAPI_BACKEND
wchar_t buf[256];
GetCurrencyFormatW(lcid, 0, L"1043.34", nullptr, buf, 256);
TEST_EQ(to_utf8(ss.str()), to_utf8<wchar_t>(buf));
#else
boost::ignore_unused(lcid);
#endif
}
{
std::cout << "--- Testing as::date/time" << std::endl;
const time_t a_date = 3600 * 24 * (31 + 4); // Feb 5th
const time_t a_time = 3600 * 15 + 60 * 33 + 13; // 15:33:13
const time_t a_datetime = a_date + a_time;
wchar_t time_buf[256]{};
wchar_t date_buf[256]{};
#ifndef BOOST_LOCALE_NO_WINAPI_BACKEND
SYSTEMTIME st = {1970, 2, 5, 5, 15, 33, 13, 0};
GetTimeFormatW(lcid, 0, &st, nullptr, time_buf, 256);
GetDateFormatW(lcid, 0, &st, nullptr, date_buf, 256);
#else
if(!time_buf[0])
return;
#endif
const std::string expDate = to_utf8(std::wstring(date_buf));
const std::string expTime = to_utf8(std::wstring(time_buf));
ss_type ss;
ss.imbue(l);
ss << as::time_zone("GMT");
empty_stream(ss) << as::date << a_datetime;
TEST_EQ(to_utf8(ss.str()), expDate);
empty_stream(ss) << as::time << a_datetime;
TEST_EQ(to_utf8(ss.str()), expTime);
empty_stream(ss) << as::datetime << a_datetime;
TEST_EQ(to_utf8(ss.str()), expDate + " " + expTime);
empty_stream(ss) << as::time_zone("GMT+01:00") << as::ftime(ascii_to<CharType>("%H")) << a_datetime;
TEST_EQ(to_utf8(ss.str()), "16");
empty_stream(ss) << as::time_zone("GMT+00:15") << as::ftime(ascii_to<CharType>("%M")) << a_datetime;
TEST_EQ(to_utf8(ss.str()), "48");
}
}
void test_date_time(const std::locale& l) // LCOV_EXCL_LINE
{
std::ostringstream ss;
ss.imbue(l);
ss << boost::locale::as::time_zone("GMT");
time_t a_date = 3600 * 24 * (31 + 4); // Feb 5th
time_t a_time = 3600 * 15 + 60 * 33; // 15:33:13
time_t a_timesec = 13;
time_t a_datetime = a_date + a_time + a_timesec;
const std::pair<std::string, std::string> testCases[] = {
{"a", "Thu"}, {"A", "Thursday"}, {"b", "Feb"}, {"B", "February"}, {"d", "05"},
{"D", "02/05/70"}, {"e", "5"}, {"h", "Feb"}, {"H", "15"}, {"I", "03"},
{"m", "02"}, {"M", "33"}, {"n", "\n"}, {"p", "PM"}, {"r", "03:33:13 PM"},
{"R", "15:33"}, {"S", "13"}, {"t", "\t"}, {"y", "70"}, {"Y", "1970"},
{"%", "%"}};
for(const auto& patternAndResult : testCases) {
empty_stream(ss) << boost::locale::as::ftime("%" + patternAndResult.first) << a_datetime;
TEST_EQ(ss.str(), patternAndResult.second);
}
} // LCOV_EXCL_LINE
BOOST_LOCALE_DISABLE_UNREACHABLE_CODE_WARNING
void test_main(int /*argc*/, char** /*argv*/)
{
#ifdef BOOST_LOCALE_NO_WINAPI_BACKEND
std::cout << "WinAPI Backend is not build... Skipping\n";
return;
#endif
boost::locale::localization_backend_manager mgr = boost::locale::localization_backend_manager::global();
mgr.select("winapi");
boost::locale::localization_backend_manager::global(mgr);
boost::locale::generator gen;
for(const auto& name_lcid : {std::make_pair("en_US.UTF-8", 0x0409),
std::make_pair("he_IL.UTF-8", 0x040D),
std::make_pair("ru_RU.UTF-8", 0x0419)})
{
const std::string name = name_lcid.first;
std::cout << "- " << name << " locale" << std::endl;
if(!has_win_locale(name))
std::cout << "-- not supported, skipping" << std::endl; // LCOV_EXCL_LINE
else {
const std::locale l = gen(name);
std::cout << "-- UTF-8" << std::endl;
test_by_char<char>(l, name, name_lcid.second);
std::cout << "-- UTF-16" << std::endl;
test_by_char<wchar_t>(l, name, name_lcid.second);
}
}
test_format_large_number();
test_parse_multi_number();
std::cout << "- Testing strftime" << std::endl;
test_date_time(gen("en_US.UTF-8"));
}
// boostinspect:noascii