// // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) // Copyright (c) 2022-2023 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt #include #include #include #include "boostLocale/test/tools.hpp" #include "boostLocale/test/unit_test.hpp" bool test_iso; const bool test_iso_8859_8 = #if defined(BOOST_LOCALE_WITH_ICU) || defined(BOOST_LOCALE_WITH_ICONV) true; #else hasWinCodepage(28598); #endif bool test_utf; bool test_sjis; std::string he_il_8bit; std::string en_us_8bit; std::string ja_jp_shiftjis; template std::basic_string read_file(std::basic_istream& in) { std::basic_string res; Char c; while(in.get(c)) res += c; return res; } template void test_ok(const std::string& content, const std::locale& l, std::basic_string cmp = std::basic_string()) { typedef std::basic_fstream stream_type; if(cmp.empty()) cmp = to(content); { const std::string file_path = boost::locale::test::exe_name + "-test_read.txt"; remove_file_on_exit _(file_path); { std::ofstream out_file(file_path, std::ios::binary); out_file << content; } stream_type in_file(file_path, stream_type::in); in_file.imbue(l); TEST_EQ(read_file(in_file), cmp); } { const std::string file_path = boost::locale::test::exe_name + "-test_write.txt"; remove_file_on_exit _(file_path); { stream_type out_file(file_path, stream_type::out); out_file.imbue(l); out_file << cmp; } std::ifstream in_file(file_path); TEST_EQ(read_file(in_file), content); } } template void test_read_fail(const std::string& content, const std::locale& l, const int pos) { const std::string file_path = boost::locale::test::exe_name + "-test.txt"; remove_file_on_exit _(file_path); { std::ofstream f(file_path, std::ios::binary); f << content; } std::basic_fstream f(file_path, std::ios::in); f.imbue(l); // Read up until the position for(int i = 0; i < pos && f; i++) { Char c; f.get(c); } // Reading may fail before the position, e.g. when the implementation reads more than the current char and hence // detects the error early if(f) { // Usually it succeeds so far and must fail now Char c; TEST(!f.get(c)); } } template void test_write_fail(const std::string& content, const std::locale& l, int pos) { const std::string file_path = boost::locale::test::exe_name + "-test.txt"; remove_file_on_exit _(file_path); typedef std::basic_fstream stream_type; stream_type f(file_path, stream_type::out); f.imbue(l); std::basic_string out = to(content); for(int i = 0; i < pos; i++) { f << out.at(i) << std::flush; TEST(f); } f << out.at(pos); TEST(f.fail() || (f << std::flush).fail()); } template void test_for_char() { boost::locale::generator g; if(test_utf) { std::cout << " UTF-8" << std::endl; test_ok("grüße\nn i", g("en_US.UTF-8")); test_read_fail("abc\xFF\xFF", g("en_US.UTF-8"), 3); std::cout << " Testing codepoints above 0xFFFF" << std::endl; std::cout << " Single U+2008A" << std::endl; test_ok("\xf0\xa0\x82\x8a", g("en_US.UTF-8")); // U+2008A std::cout << " Single U+2008A within text" << std::endl; test_ok("abc\"\xf0\xa0\x82\x8a\"", g("en_US.UTF-8")); // U+2008A std::string one = "\xf0\xa0\x82\x8a"; std::string res; for(unsigned i = 0; i < 1000; i++) res += one; std::cout << " U+2008A x 1000" << std::endl; test_ok(res.c_str(), g("en_US.UTF-8")); // U+2008A } if(test_iso) { if(test_iso_8859_8) { std::cout << " ISO8859-8" << std::endl; test_ok("hello \xf9\xec\xe5\xed", g(he_il_8bit), to("hello שלום")); } std::cout << " ISO8859-1" << std::endl; test_ok(to("grüße\nn i"), g(en_us_8bit), to("grüße\nn i")); test_write_fail("grüßen שלום", g(en_us_8bit), 7); } if(test_sjis) { std::cout << " Shift-JIS" << std::endl; test_ok("\x93\xfa\x96\x7b", g(ja_jp_shiftjis), boost::locale::conv::utf_to_utf("\xe6\x97\xa5\xe6\x9c\xac")); // Japan } } void test_wide_io() { std::cout << " wchar_t" << std::endl; test_for_char(); // std::codecvt(); #endif #if defined BOOST_LOCALE_ENABLE_CHAR32_T std::cout << " char32_t" << std::endl; test_for_char(); #endif } void test_main(int /*argc*/, char** /*argv*/) { for(const std::string& backendName : boost::locale::localization_backend_manager::global().get_all_backends()) { boost::locale::localization_backend_manager tmp_backend = boost::locale::localization_backend_manager::global(); tmp_backend.select(backendName); boost::locale::localization_backend_manager::global(tmp_backend); en_us_8bit = "en_US.ISO8859-1"; he_il_8bit = "he_IL.ISO8859-8"; ja_jp_shiftjis = "ja_JP.SJIS"; if(backendName == "std") { en_us_8bit = get_std_name(en_us_8bit); he_il_8bit = get_std_name(he_il_8bit); std::string real_ja_jp_shiftjis; ja_jp_shiftjis = get_std_name(ja_jp_shiftjis, &real_ja_jp_shiftjis); if(!ja_jp_shiftjis.empty() && !test_std_supports_SJIS_codecvt(real_ja_jp_shiftjis)) ja_jp_shiftjis = ""; // LCOV_EXCL_LINE } std::cout << "Testing for backend " << backendName << std::endl; if(backendName == "std") { test_iso = !he_il_8bit.empty() && !en_us_8bit.empty(); test_sjis = !ja_jp_shiftjis.empty(); test_utf = !get_std_name("en_US.UTF-8").empty() && !get_std_name("he_IL.UTF-8").empty(); } else if(backendName == "winapi") { test_iso = false; test_sjis = false; test_utf = true; } else if(backendName == "posix") { #ifdef BOOST_LOCALE_NO_POSIX_BACKEND throw std::logic_error("Unexpected backend"); // LCOV_EXCL_LINE #else test_iso = has_posix_locale(he_il_8bit) && has_posix_locale(en_us_8bit); test_utf = has_posix_locale("en_US.UTF-8"); # ifdef BOOST_LOCALE_WITH_ICONV test_sjis = has_posix_locale(ja_jp_shiftjis); # else test_sjis = false; # endif #endif } else { test_iso = true; test_sjis = true; test_utf = true; } test_wide_io(); } } // boostinspect:noascii