1//
2// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
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/encoding.hpp>
8#include <boost/locale/formatting.hpp>
9#include <boost/locale/generator.hpp>
10#include <boost/locale/info.hpp>
11#include <boost/locale/localization_backend.hpp>
12#include <boost/core/ignore_unused.hpp>
13#include <ctime>
14#include <iomanip>
15#include <iostream>
16#ifndef BOOST_LOCALE_NO_POSIX_BACKEND
17# include <langinfo.h>
18# include <monetary.h>
19#endif
20#include "boostLocale/test/tools.hpp"
21#include "boostLocale/test/unit_test.hpp"
22
23#ifdef BOOST_LOCALE_NO_POSIX_BACKEND
24// Dummy just to make it compile
25size_t strftime_l(char*, size_t, const char*, const std::tm*, locale_t)
26{
27 return 0; // LCOV_EXCL_LINE
28}
29#endif
30
31template<typename CharType>
32std::basic_string<CharType> from_narrow(const std::string& s, locale_t lc)
33{
34#ifdef BOOST_LOCALE_NO_POSIX_BACKEND
35 const std::string charset; // LCOV_EXCL_LINE
36 boost::ignore_unused(lc); // LCOV_EXCL_LINE
37#else
38 const std::string charset = nl_langinfo_l(CODESET, l: lc);
39#endif
40 return boost::locale::conv::to_utf<CharType>(s, charset);
41}
42
43template<>
44std::basic_string<char> from_narrow(const std::string& s, locale_t)
45{
46 return s;
47}
48
49template<typename CharType>
50void test_by_char(const std::locale& l, locale_t lreal)
51{
52 typedef std::basic_stringstream<CharType> ss_type;
53
54 using namespace boost::locale;
55
56 {
57 std::cout << "- Testing as::posix" << std::endl;
58 ss_type ss;
59 ss.imbue(l);
60
61 TEST(ss << 1045.45);
62 double n;
63 TEST(ss >> n);
64 TEST_EQ(n, 1045.45);
65 TEST_EQ(ss.str(), ascii_to<CharType>("1045.45"));
66 }
67
68 {
69 std::cout << "- Testing as::number" << std::endl;
70 ss_type ss;
71 ss.imbue(l);
72
73 ss << as::number;
74 TEST(ss << 1045.45);
75 double n;
76 TEST(ss >> n);
77 TEST_EQ(n, 1045.45);
78
79 if(std::use_facet<boost::locale::info>(loc: l).country() == "US")
80 TEST_EQ(ss.str(), from_narrow<CharType>("1,045.45", lreal));
81 }
82
83 {
84 std::cout << "- Testing as::currency national " << std::endl;
85
86 char buf[64]{};
87#ifndef BOOST_LOCALE_NO_POSIX_BACKEND
88 TEST_GT(strfmon_l(buf, sizeof(buf), lreal, "%n", 1043.34), 0);
89#endif
90
91 ss_type ss;
92 ss.imbue(l);
93
94 ss << as::currency;
95 TEST(ss << 1043.34);
96
97 TEST_EQ(ss.str(), from_narrow<CharType>(buf, lreal));
98 }
99
100 {
101 std::cout << "- Testing as::currency iso" << std::endl;
102 char buf[64]{};
103#ifndef BOOST_LOCALE_NO_POSIX_BACKEND
104 TEST_GT(strfmon_l(buf, sizeof(buf), lreal, "%i", 1043.34), 0);
105#endif
106 ss_type ss;
107 ss.imbue(l);
108
109 ss << as::currency << as::currency_iso;
110 TEST(ss << 1043.34);
111
112 TEST_EQ(ss.str(), from_narrow<CharType>(buf, lreal));
113 }
114
115 {
116 std::cout << "- Testing as::date/time" << std::endl;
117
118 const time_t a_date = 3600 * 24 * (31 + 4); // Feb 5th
119 const time_t a_time = 3600 * 15 + 60 * 33 + 13; // 15:33:13
120 const time_t a_datetime = a_date + a_time;
121
122 char buf[64]{};
123 const std::tm tm = *gmtime_wrap(time: &a_datetime);
124 TEST_REQUIRE(strftime_l(buf, sizeof(buf), "%x", &tm, lreal) != 0u);
125 const auto expDate = from_narrow<CharType>(buf, lreal);
126 TEST_REQUIRE(strftime_l(buf, sizeof(buf), "%X", &tm, lreal) != 0u);
127 const auto expTime = from_narrow<CharType>(buf, lreal);
128 TEST_REQUIRE(strftime_l(buf, sizeof(buf), "%c", &tm, lreal) != 0u);
129 const auto expDateTime = from_narrow<CharType>(buf, lreal);
130
131 ss_type ss;
132 ss.imbue(l);
133 ss << as::time_zone(id: "GMT");
134
135 empty_stream(ss) << as::date << a_datetime;
136 TEST_EQ(ss.str(), expDate);
137 empty_stream(ss) << as::time << a_datetime;
138 TEST_EQ(ss.str(), expTime);
139 empty_stream(ss) << as::datetime << a_datetime;
140 TEST_EQ(ss.str(), expDateTime);
141 empty_stream(ss) << as::time_zone(id: "GMT+01:00") << as::ftime(ascii_to<CharType>("%H")) << a_datetime;
142 TEST_EQ(ss.str(), ascii_to<CharType>("16"));
143 empty_stream(ss) << as::time_zone(id: "GMT+00:15") << as::ftime(ascii_to<CharType>("%M")) << a_datetime;
144 TEST_EQ(ss.str(), ascii_to<CharType>("48"));
145 }
146}
147
148BOOST_LOCALE_DISABLE_UNREACHABLE_CODE_WARNING
149void test_main(int /*argc*/, char** /*argv*/)
150{
151#ifdef BOOST_LOCALE_NO_POSIX_BACKEND
152 std::cout << "POSIX Backend is not build... Skipping\n";
153 return;
154#endif
155 boost::locale::localization_backend_manager mgr = boost::locale::localization_backend_manager::global();
156 mgr.select(backend_name: "posix");
157 boost::locale::localization_backend_manager::global(mgr);
158 boost::locale::generator gen;
159 for(const std::string locale_name : {"en_US.UTF-8", "en_US.ISO8859-1", "he_IL.UTF-8", "he_IL.ISO8859-8"}) {
160 std::cout << locale_name << " locale" << std::endl;
161 if(!has_posix_locale(name: locale_name))
162 std::cout << locale_name << " not supported, skipping" << std::endl; // LCOV_EXCL_LINE
163 else {
164 std::locale generated_locale = gen(locale_name);
165 locale_holder real_locale(newlocale(LC_ALL_MASK, locale: locale_name.c_str(), base: nullptr));
166 TEST_REQUIRE(real_locale);
167
168 std::cout << "UTF-8" << std::endl;
169 test_by_char<char>(l: generated_locale, lreal: real_locale);
170
171 std::cout << "Wide UTF-" << sizeof(wchar_t) * 8 << std::endl;
172 test_by_char<wchar_t>(l: generated_locale, lreal: real_locale);
173 }
174 }
175 {
176 std::cout << "Testing UTF-8 punct issues" << std::endl;
177 const std::string locale_name = "ru_RU.UTF-8";
178 if(!has_posix_locale(name: locale_name))
179 std::cout << "- No Russian locale, skipping" << std::endl; // LCOV_EXCL_LINE
180 else {
181 std::ostringstream ss;
182 ss.imbue(loc: gen(locale_name));
183 ss << std::setprecision(10) << boost::locale::as::number << 12345.45;
184 const std::string v = ss.str();
185 TEST(v == "12345,45" || v == "12 345,45" || v == "12.345,45");
186 }
187 }
188}
189
190// boostinspect:noascii
191

source code of boost/libs/locale/test/test_posix_formatting.cpp