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 | |
15 | bool test_iso; |
16 | const 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 |
22 | bool test_utf; |
23 | bool test_sjis; |
24 | |
25 | std::string he_il_8bit; |
26 | std::string en_us_8bit; |
27 | std::string ja_jp_shiftjis; |
28 | |
29 | template<typename Char> |
30 | std::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 | |
39 | template<typename Char> |
40 | void 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 | |
71 | template<typename Char> |
72 | void 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 | |
97 | template<typename Char> |
98 | void 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 | |
115 | template<typename Char> |
116 | void 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 | } |
153 | void 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 | |
170 | void 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 | |