Skip to content

Commit 34fef7f

Browse files
committed
Add tests for boost::locale::numpunct
1 parent 4e28209 commit 34fef7f

File tree

4 files changed

+153
-0
lines changed

4 files changed

+153
-0
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ jobs:
253253
254254
gen_locale en_US.UTF-8 # Assumed to be there by tests
255255
# Used by various tests
256+
gen_locale de_DE.UTF-8
256257
gen_locale he_IL.UTF-8
257258
gen_locale ja_JP.UTF-8
258259
gen_locale ru_RU.UTF-8

test/Jamfile.v2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ run test_codecvt.cpp ;
4444
run test_codepage_converter.cpp ;
4545
run test_stream_io.cpp ;
4646
run test_message.cpp : $(BOOST_ROOT)/libs/locale/test ;
47+
run test_numpunct.cpp ;
4748
run test_generator.cpp ;
4849
# icu
4950
run test_collate.cpp ;

test/test_generator.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ void test_main(int /*argc*/, char** /*argv*/)
375375
TEST_HAS_FACETS(std::num_put, l);
376376
TEST_HAS_FACETS(std::time_put, l);
377377
TEST_HAS_FACETS(std::numpunct, l);
378+
TEST_HAS_FACETS(bl::numpunct, l);
378379
TEST_HAS_FACETS(std::moneypunct, l);
379380
// Parsing
380381
TEST_HAS_FACETS(std::num_get, l);

test/test_numpunct.cpp

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//
2+
// Copyright (c) 2023 Alexander Grund
3+
//
4+
// Distributed under the Boost Software License, Version 1.0.
5+
// https://www.boost.org/LICENSE_1_0.txt
6+
7+
#include <boost/locale.hpp>
8+
#include <boost/locale/util/string.hpp>
9+
#include "boostLocale/test/tools.hpp"
10+
#include "boostLocale/test/unit_test.hpp"
11+
#include <limits>
12+
#include <map>
13+
#include <sstream>
14+
#include <string>
15+
16+
namespace bl = boost::locale;
17+
18+
template<typename Char>
19+
void as_if_std_numpunct(const std::locale& l)
20+
{
21+
const std::numpunct<Char>& std_facet = std::use_facet<std::numpunct<Char>>(l);
22+
const bl::numpunct<Char>& boost_facet = std::use_facet<bl::numpunct<Char>>(l);
23+
// All functions present in std::numpunct are also present in boost::locale::numpunct and yield the same results
24+
TEST_REQUIRE(dynamic_cast<const bl::numpunct<Char>*>(&std_facet)); // In fact they are equal
25+
TEST_EQ(std_facet.decimal_point(), boost_facet.decimal_point());
26+
TEST_EQ(std_facet.thousands_sep(), boost_facet.thousands_sep());
27+
TEST_EQ(std_facet.grouping(), boost_facet.grouping());
28+
TEST_EQ(std_facet.truename(), boost_facet.truename());
29+
TEST_EQ(std_facet.falsename(), boost_facet.falsename());
30+
}
31+
32+
namespace {
33+
template<typename Char = char>
34+
struct Punctuation {
35+
std::basic_string<Char> decimal;
36+
std::basic_string<Char> thousand;
37+
};
38+
39+
const std::map<std::string, Punctuation<>> expected_punctuations = {
40+
{"en", {".", ","}},
41+
{"de", {",", "."}},
42+
{"he", {".", ","}},
43+
{"ja", {".", ","}},
44+
{"ru", {",", "\xC2\xA0"}},
45+
{"it", {".", ","}},
46+
};
47+
48+
template<typename Char, template<typename> class Res>
49+
Res<Char> get_expected(const std::map<std::string, Res<char>>& from, const std::locale& l)
50+
{
51+
const auto& src = from.at(std::use_facet<bl::info>(l).language());
52+
return {to_correct_string<Char>(src.decimal, l), to_correct_string<Char>(src.thousand, l)};
53+
}
54+
55+
template<typename Char>
56+
struct split_result {
57+
std::basic_string<Char> digits, others;
58+
};
59+
60+
template<typename Char>
61+
split_result<Char> split_number(const std::basic_string<Char>& s)
62+
{
63+
split_result<Char> res;
64+
for(Char c : s) {
65+
if(c >= std::numeric_limits<char>::min() && c <= std::numeric_limits<char>::max()
66+
&& boost::locale::util::is_numeric_ascii(static_cast<char>(c)))
67+
res.digits += c;
68+
else
69+
res.others += c;
70+
}
71+
return res;
72+
}
73+
} // namespace
74+
75+
template<typename Char>
76+
void test_for_char(const std::locale& l)
77+
{
78+
using string_type = std::basic_string<Char>;
79+
as_if_std_numpunct<Char>(l);
80+
{
81+
const auto& expected = get_expected<Char>(expected_punctuations, l);
82+
const auto& boost_facet = std::use_facet<bl::numpunct<Char>>(l);
83+
TEST_EQ(boost_facet.decimal_point_str(), expected.decimal);
84+
TEST_EQ(boost_facet.thousands_sep_str(), expected.thousand);
85+
}
86+
std::basic_ostringstream<Char> s;
87+
s.imbue(l);
88+
// Formatting not using the Boost.Locale modifiers uses (only) the std::numpunct values
89+
{
90+
const auto& facet = std::use_facet<std::numpunct<Char>>(l);
91+
empty_stream(s) << 1234567890;
92+
auto actual = split_number(s.str());
93+
TEST_EQ(actual.digits, ascii_to<Char>("1234567890"));
94+
TEST_EQ(actual.others, string_type(actual.others.size(), facet.thousands_sep()));
95+
96+
empty_stream(s) << 12.25;
97+
actual = split_number(s.str());
98+
TEST_EQ(actual.digits, ascii_to<Char>("1225"));
99+
TEST_EQ(actual.others, string_type(actual.others.size(), facet.decimal_point()));
100+
}
101+
// Formatting using the Boost.Locale modifiers uses the boost::locale::numpunct values
102+
s << bl::as::number;
103+
{
104+
const auto& facet = std::use_facet<bl::numpunct<Char>>(l);
105+
empty_stream(s) << 1234567890;
106+
auto actual = split_number(s.str());
107+
TEST_EQ(actual.digits, ascii_to<Char>("1234567890"));
108+
TEST_EQ(actual.others, string_type(actual.others.size(), facet.thousands_sep()));
109+
110+
empty_stream(s) << 12.25;
111+
actual = split_number(s.str());
112+
TEST_EQ(actual.digits, ascii_to<Char>("1225"));
113+
TEST_EQ(actual.others, string_type(actual.others.size(), facet.decimal_point()));
114+
}
115+
}
116+
117+
void test_for_locale(const std::string& name)
118+
{
119+
std::cout << "-- Locale: " << name << '\n';
120+
const std::locale l = bl::generator{}(name);
121+
std::cout << "---- char\n";
122+
test_for_char<char>(l);
123+
std::cout << "---- wchar_t\n";
124+
test_for_char<wchar_t>(l);
125+
#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
126+
std::cout << "---- char16_t\n";
127+
test_for_char<char16_t>(l);
128+
#endif
129+
#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
130+
std::cout << "---- char32_t\n";
131+
test_for_char<char32_t>(l);
132+
#endif
133+
}
134+
135+
void test_main(int /*argc*/, char** /*argv*/)
136+
{
137+
const bl::localization_backend_manager orig_backend = bl::localization_backend_manager::global();
138+
for(const std::string& backendName : orig_backend.get_all_backends()) {
139+
std::cout << "Backend: " << backendName << std::endl;
140+
bl::localization_backend_manager tmp_backend = bl::localization_backend_manager::global();
141+
tmp_backend.select(backendName);
142+
bl::localization_backend_manager::global(tmp_backend);
143+
test_for_locale("en_US.UTF-8");
144+
test_for_locale("de_DE.UTF-8");
145+
test_for_locale("he_IL.UTF-8");
146+
test_for_locale("ja_JP.UTF-8");
147+
test_for_locale("ru_RU.UTF-8");
148+
test_for_locale("it_IT");
149+
}
150+
}

0 commit comments

Comments
 (0)