1//
2// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
3// Copyright (c) 2022-2023 Alexander Grund
4//
5// Distributed under the Boost Software License, Version 1.0.
6// https://www.boost.org/LICENSE_1_0.txt
7
8#include <boost/locale/generator.hpp>
9#include <boost/locale/localization_backend.hpp>
10#include <fstream>
11
12#include "boostLocale/test/tools.hpp"
13#include "boostLocale/test/unit_test.hpp"
14
15bool test_iso;
16const bool test_iso_8859_8 =
17#if defined(BOOST_LOCALE_WITH_ICU) || defined(BOOST_LOCALE_WITH_ICONV)
18 true;
19#else
20 hasWinCodepage(28598);
21#endif
22bool test_utf;
23bool test_sjis;
24
25std::string he_il_8bit;
26std::string en_us_8bit;
27std::string ja_jp_shiftjis;
28
29template<typename Char>
30std::basic_string<Char> read_file(std::basic_istream<Char>& in)
31{
32 std::basic_string<Char> res;
33 Char c;
34 while(in.get(c))
35 res += c;
36 return res;
37}
38
39template<typename Char>
40void test_ok(const std::string& content, const std::locale& l, std::basic_string<Char> cmp = std::basic_string<Char>())
41{
42 typedef std::basic_fstream<Char> stream_type;
43 if(cmp.empty())
44 cmp = to<Char>(content);
45
46 {
47 const std::string file_path = boost::locale::test::exe_name + "-test_read.txt";
48 remove_file_on_exit _(file_path);
49 {
50 std::ofstream out_file(file_path, std::ios::binary);
51 out_file << content;
52 }
53 stream_type in_file(file_path, stream_type::in);
54 in_file.imbue(l);
55 TEST_EQ(read_file<Char>(in_file), cmp);
56 }
57
58 {
59 const std::string file_path = boost::locale::test::exe_name + "-test_write.txt";
60 remove_file_on_exit _(file_path);
61 {
62 stream_type out_file(file_path, stream_type::out);
63 out_file.imbue(l);
64 out_file << cmp;
65 }
66 std::ifstream in_file(file_path);
67 TEST_EQ(read_file<char>(in_file), content);
68 }
69}
70
71template<typename Char>
72void test_read_fail(const std::string& content, const std::locale& l, const int pos)
73{
74 const std::string file_path = boost::locale::test::exe_name + "-test.txt";
75 remove_file_on_exit _(file_path);
76 {
77 std::ofstream f(file_path, std::ios::binary);
78 f << content;
79 }
80
81 std::basic_fstream<Char> f(file_path, std::ios::in);
82 f.imbue(l);
83 // Read up until the position
84 for(int i = 0; i < pos && f; i++) {
85 Char c;
86 f.get(c);
87 }
88 // Reading may fail before the position, e.g. when the implementation reads more than the current char and hence
89 // detects the error early
90 if(f) {
91 // Usually it succeeds so far and must fail now
92 Char c;
93 TEST(!f.get(c));
94 }
95}
96
97template<typename Char>
98void test_write_fail(const std::string& content, const std::locale& l, int pos)
99{
100 const std::string file_path = boost::locale::test::exe_name + "-test.txt";
101 remove_file_on_exit _(file_path);
102
103 typedef std::basic_fstream<Char> stream_type;
104 stream_type f(file_path, stream_type::out);
105 f.imbue(l);
106 std::basic_string<Char> out = to<Char>(content);
107 for(int i = 0; i < pos; i++) {
108 f << out.at(i) << std::flush;
109 TEST(f);
110 }
111 f << out.at(pos);
112 TEST(f.fail() || (f << std::flush).fail());
113}
114
115template<typename Char>
116void test_for_char()
117{
118 boost::locale::generator g;
119 if(test_utf) {
120 std::cout << " UTF-8" << std::endl;
121 test_ok<Char>("grüße\nn i", g("en_US.UTF-8"));
122 test_read_fail<Char>("abc\xFF\xFF", g("en_US.UTF-8"), 3);
123 std::cout << " Testing codepoints above 0xFFFF" << std::endl;
124 std::cout << " Single U+2008A" << std::endl;
125 test_ok<Char>("\xf0\xa0\x82\x8a", g("en_US.UTF-8")); // U+2008A
126 std::cout << " Single U+2008A within text" << std::endl;
127 test_ok<Char>("abc\"\xf0\xa0\x82\x8a\"", g("en_US.UTF-8")); // U+2008A
128 std::string one = "\xf0\xa0\x82\x8a";
129 std::string res;
130 for(unsigned i = 0; i < 1000; i++)
131 res += one;
132 std::cout << " U+2008A x 1000" << std::endl;
133 test_ok<Char>(res.c_str(), g("en_US.UTF-8")); // U+2008A
134 }
135
136 if(test_iso) {
137 if(test_iso_8859_8) {
138 std::cout << " ISO8859-8" << std::endl;
139 test_ok<Char>("hello \xf9\xec\xe5\xed", g(he_il_8bit), to<Char>("hello שלום"));
140 }
141 std::cout << " ISO8859-1" << std::endl;
142 test_ok<Char>(to<char>(utf8: "grüße\nn i"), g(en_us_8bit), to<Char>("grüße\nn i"));
143 test_write_fail<Char>("grüßen שלום", g(en_us_8bit), 7);
144 }
145
146 if(test_sjis) {
147 std::cout << " Shift-JIS" << std::endl;
148 test_ok<Char>("\x93\xfa\x96\x7b",
149 g(ja_jp_shiftjis),
150 boost::locale::conv::utf_to_utf<Char>("\xe6\x97\xa5\xe6\x9c\xac")); // Japan
151 }
152}
153void test_wide_io()
154{
155 std::cout << " wchar_t" << std::endl;
156 test_for_char<wchar_t>();
157
158 // std::codecvt<char8_t doesn't have proper library support (e.g. MSVC doesn't export the id)
159
160#if defined BOOST_LOCALE_ENABLE_CHAR16_T
161 std::cout << " char16_t" << std::endl;
162 test_for_char<char16_t>();
163#endif
164#if defined BOOST_LOCALE_ENABLE_CHAR32_T
165 std::cout << " char32_t" << std::endl;
166 test_for_char<char32_t>();
167#endif
168}
169
170void test_main(int /*argc*/, char** /*argv*/)
171{
172 for(const std::string& backendName : boost::locale::localization_backend_manager::global().get_all_backends()) {
173 boost::locale::localization_backend_manager tmp_backend = boost::locale::localization_backend_manager::global();
174 tmp_backend.select(backend_name: backendName);
175 boost::locale::localization_backend_manager::global(tmp_backend);
176
177 en_us_8bit = "en_US.ISO8859-1";
178 he_il_8bit = "he_IL.ISO8859-8";
179 ja_jp_shiftjis = "ja_JP.SJIS";
180 if(backendName == "std") {
181 en_us_8bit = get_std_name(name: en_us_8bit);
182 he_il_8bit = get_std_name(name: he_il_8bit);
183 std::string real_ja_jp_shiftjis;
184 ja_jp_shiftjis = get_std_name(name: ja_jp_shiftjis, real_name: &real_ja_jp_shiftjis);
185 if(!ja_jp_shiftjis.empty() && !test_std_supports_SJIS_codecvt(locale_name: real_ja_jp_shiftjis))
186 ja_jp_shiftjis = ""; // LCOV_EXCL_LINE
187 }
188
189 std::cout << "Testing for backend " << backendName << std::endl;
190
191 if(backendName == "std") {
192 test_iso = !he_il_8bit.empty() && !en_us_8bit.empty();
193 test_sjis = !ja_jp_shiftjis.empty();
194 test_utf = !get_std_name(name: "en_US.UTF-8").empty() && !get_std_name(name: "he_IL.UTF-8").empty();
195 } else if(backendName == "winapi") {
196 test_iso = false;
197 test_sjis = false;
198 test_utf = true;
199 } else if(backendName == "posix") {
200#ifdef BOOST_LOCALE_NO_POSIX_BACKEND
201 throw std::logic_error("Unexpected backend"); // LCOV_EXCL_LINE
202#else
203 test_iso = has_posix_locale(name: he_il_8bit) && has_posix_locale(name: en_us_8bit);
204 test_utf = has_posix_locale(name: "en_US.UTF-8");
205# ifdef BOOST_LOCALE_WITH_ICONV
206 test_sjis = has_posix_locale(name: ja_jp_shiftjis);
207# else
208 test_sjis = false;
209# endif
210#endif
211 } else {
212 test_iso = true;
213 test_sjis = true;
214 test_utf = true;
215 }
216
217 test_wide_io();
218 }
219}
220
221// boostinspect:noascii
222

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