From 34fef7f37aea98a4039cdd68127b7c300d084270 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Sun, 21 May 2023 15:17:30 +0200 Subject: [PATCH] Add tests for `boost::locale::numpunct` --- .github/workflows/ci.yml | 1 + test/Jamfile.v2 | 1 + test/test_generator.cpp | 1 + test/test_numpunct.cpp | 150 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 test/test_numpunct.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43f4b935..3e33b53b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -253,6 +253,7 @@ jobs: gen_locale en_US.UTF-8 # Assumed to be there by tests # Used by various tests + gen_locale de_DE.UTF-8 gen_locale he_IL.UTF-8 gen_locale ja_JP.UTF-8 gen_locale ru_RU.UTF-8 diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 40f4b6f1..7e3a1263 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -44,6 +44,7 @@ run test_codecvt.cpp ; run test_codepage_converter.cpp ; run test_stream_io.cpp ; run test_message.cpp : $(BOOST_ROOT)/libs/locale/test ; +run test_numpunct.cpp ; run test_generator.cpp ; # icu run test_collate.cpp ; diff --git a/test/test_generator.cpp b/test/test_generator.cpp index 084101bf..982cf9aa 100644 --- a/test/test_generator.cpp +++ b/test/test_generator.cpp @@ -375,6 +375,7 @@ void test_main(int /*argc*/, char** /*argv*/) TEST_HAS_FACETS(std::num_put, l); TEST_HAS_FACETS(std::time_put, l); TEST_HAS_FACETS(std::numpunct, l); + TEST_HAS_FACETS(bl::numpunct, l); TEST_HAS_FACETS(std::moneypunct, l); // Parsing TEST_HAS_FACETS(std::num_get, l); diff --git a/test/test_numpunct.cpp b/test/test_numpunct.cpp new file mode 100644 index 00000000..0ee0909b --- /dev/null +++ b/test/test_numpunct.cpp @@ -0,0 +1,150 @@ +// +// Copyright (c) 2023 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include "boostLocale/test/tools.hpp" +#include "boostLocale/test/unit_test.hpp" +#include +#include +#include +#include + +namespace bl = boost::locale; + +template +void as_if_std_numpunct(const std::locale& l) +{ + const std::numpunct& std_facet = std::use_facet>(l); + const bl::numpunct& boost_facet = std::use_facet>(l); + // All functions present in std::numpunct are also present in boost::locale::numpunct and yield the same results + TEST_REQUIRE(dynamic_cast*>(&std_facet)); // In fact they are equal + TEST_EQ(std_facet.decimal_point(), boost_facet.decimal_point()); + TEST_EQ(std_facet.thousands_sep(), boost_facet.thousands_sep()); + TEST_EQ(std_facet.grouping(), boost_facet.grouping()); + TEST_EQ(std_facet.truename(), boost_facet.truename()); + TEST_EQ(std_facet.falsename(), boost_facet.falsename()); +} + +namespace { +template +struct Punctuation { + std::basic_string decimal; + std::basic_string thousand; +}; + +const std::map> expected_punctuations = { + {"en", {".", ","}}, + {"de", {",", "."}}, + {"he", {".", ","}}, + {"ja", {".", ","}}, + {"ru", {",", "\xC2\xA0"}}, + {"it", {".", ","}}, +}; + +template class Res> +Res get_expected(const std::map>& from, const std::locale& l) +{ + const auto& src = from.at(std::use_facet(l).language()); + return {to_correct_string(src.decimal, l), to_correct_string(src.thousand, l)}; +} + +template +struct split_result { + std::basic_string digits, others; +}; + +template +split_result split_number(const std::basic_string& s) +{ + split_result res; + for(Char c : s) { + if(c >= std::numeric_limits::min() && c <= std::numeric_limits::max() + && boost::locale::util::is_numeric_ascii(static_cast(c))) + res.digits += c; + else + res.others += c; + } + return res; +} +} // namespace + +template +void test_for_char(const std::locale& l) +{ + using string_type = std::basic_string; + as_if_std_numpunct(l); + { + const auto& expected = get_expected(expected_punctuations, l); + const auto& boost_facet = std::use_facet>(l); + TEST_EQ(boost_facet.decimal_point_str(), expected.decimal); + TEST_EQ(boost_facet.thousands_sep_str(), expected.thousand); + } + std::basic_ostringstream s; + s.imbue(l); + // Formatting not using the Boost.Locale modifiers uses (only) the std::numpunct values + { + const auto& facet = std::use_facet>(l); + empty_stream(s) << 1234567890; + auto actual = split_number(s.str()); + TEST_EQ(actual.digits, ascii_to("1234567890")); + TEST_EQ(actual.others, string_type(actual.others.size(), facet.thousands_sep())); + + empty_stream(s) << 12.25; + actual = split_number(s.str()); + TEST_EQ(actual.digits, ascii_to("1225")); + TEST_EQ(actual.others, string_type(actual.others.size(), facet.decimal_point())); + } + // Formatting using the Boost.Locale modifiers uses the boost::locale::numpunct values + s << bl::as::number; + { + const auto& facet = std::use_facet>(l); + empty_stream(s) << 1234567890; + auto actual = split_number(s.str()); + TEST_EQ(actual.digits, ascii_to("1234567890")); + TEST_EQ(actual.others, string_type(actual.others.size(), facet.thousands_sep())); + + empty_stream(s) << 12.25; + actual = split_number(s.str()); + TEST_EQ(actual.digits, ascii_to("1225")); + TEST_EQ(actual.others, string_type(actual.others.size(), facet.decimal_point())); + } +} + +void test_for_locale(const std::string& name) +{ + std::cout << "-- Locale: " << name << '\n'; + const std::locale l = bl::generator{}(name); + std::cout << "---- char\n"; + test_for_char(l); + std::cout << "---- wchar_t\n"; + test_for_char(l); +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T + std::cout << "---- char16_t\n"; + test_for_char(l); +#endif +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T + std::cout << "---- char32_t\n"; + test_for_char(l); +#endif +} + +void test_main(int /*argc*/, char** /*argv*/) +{ + const bl::localization_backend_manager orig_backend = bl::localization_backend_manager::global(); + for(const std::string& backendName : orig_backend.get_all_backends()) { + std::cout << "Backend: " << backendName << std::endl; + bl::localization_backend_manager tmp_backend = bl::localization_backend_manager::global(); + tmp_backend.select(backendName); + bl::localization_backend_manager::global(tmp_backend); + test_for_locale("en_US.UTF-8"); + test_for_locale("de_DE.UTF-8"); + test_for_locale("he_IL.UTF-8"); + test_for_locale("ja_JP.UTF-8"); + test_for_locale("ru_RU.UTF-8"); + test_for_locale("it_IT"); + } +}