| 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 | |