Author: brane Date: Thu Dec 27 21:09:24 2018 New Revision: 1849816 URL: http://svn.apache.org/viewvc?rev=1849816&view=rev Log: Added string conversion tests to SVN++.
* subversion/bindings/cxx/tests/test_strings.cpp: New. Added: subversion/trunk/subversion/bindings/cxx/tests/test_strings.cpp (with props) Added: subversion/trunk/subversion/bindings/cxx/tests/test_strings.cpp URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/tests/test_strings.cpp?rev=1849816&view=auto ============================================================================== --- subversion/trunk/subversion/bindings/cxx/tests/test_strings.cpp (added) +++ subversion/trunk/subversion/bindings/cxx/tests/test_strings.cpp Thu Dec 27 21:09:24 2018 @@ -0,0 +1,217 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +#include <boost/test/unit_test.hpp> + +#include <codecvt> +#include <cstdint> +#include <locale> +#include <random> +#include <stdexcept> +#include <string> +#include <vector> + +#include "private/svn_utf_private.h" +#include "../src/aprwrap.hpp" + +namespace { +std::string to_utf8(const std::u32string& str) +{ + static const int32_t endiancheck = 0xa5cbbc5a; + static const bool arch_big_endian = + (reinterpret_cast<const char*>(&endiancheck)[sizeof(endiancheck) - 1] == '\x5a'); + + apr::pool scratch_pool; + const svn_string_t* utf8_string; + + auto err = svn_utf__utf32_to_utf8( + &utf8_string, + reinterpret_cast<const apr_int32_t*>(str.c_str()), + str.size(), arch_big_endian, scratch_pool.get(), scratch_pool.get()); + if (err) + { + svn_error_clear(err); + throw std::range_error("bad unicode code point"); + } + return std::string(utf8_string->data, utf8_string->len); +} + +template<typename C> struct codepoint; +template<> struct codepoint<void> +{ + using src_type = char32_t; + static constexpr std::uint_least32_t min = 0; + static constexpr std::uint_least32_t max = 0x10ffff; + static constexpr std::uint_least32_t surrogate_min = 0xd800; + static constexpr std::uint_least32_t surrogate_max = 0xdfff; +}; + +template<> struct codepoint<char32_t> : public codepoint<void> +{ + using dst_type = char32_t; + static std::u32string convert(const std::u32string& str) + { + return str; + }; +}; + +template<> struct codepoint<char16_t> : public codepoint<void> +{ + using dst_type = char16_t; + static std::u16string convert(const std::u32string& str) + { + std::wstring_convert<std::codecvt_utf8_utf16<dst_type>, dst_type> u; + return u.from_bytes(to_utf8(str)); + } +}; + +template<> struct codepoint<wchar_t> : public codepoint<void> +{ + using dst_type = wchar_t; + +#ifdef WIN32 + // Be conservative, use UCS-2 for wchar_t on Windows + static_assert(sizeof(wchar_t) == sizeof(char16_t), + "I thought we had 2-byte wide chars on Windows"); + static constexpr std::uint_least32_t max = 0xffff; +#endif + + static std::wstring convert(const std::u32string& str) + { +#ifdef WIN32 + const auto from_utf8 = + [](const std::string& sstr) + { + apr::pool scratch_pool; + const wchar_t* result; + auto err = svn_utf__win32_utf8_to_utf16( + &result, sstr.c_str(), nullptr, scratch_pool.get()); + if (err) + { + svn_error_clear(err); + throw std::range_error("bad conversion to utf16"); + } + return std::wstring(result); + } +#else + std::wstring_convert<std::codecvt_utf8<dst_type>, dst_type> u; + const auto from_utf8 = [&u](const std::string& sstr) + { + return u.from_bytes(sstr); + }; +#endif + return from_utf8(to_utf8(str)); + } +}; + +// Generate random strings. +template<typename C> +inline std::vector<std::basic_string<C>> generate_string_data(int count) +{ + using cp = codepoint<C>; + std::mt19937 mt{std::random_device()()}; + std::uniform_int_distribution<> cgen{typename cp::src_type(cp::min), + typename cp::src_type(cp::max)}; + std::uniform_int_distribution<> lgen{7U, 31U}; + + std::vector<std::basic_string<C>> result; + result.reserve(count); + + for (int i = 0; i < count; ++i) + { + const unsigned len = lgen(mt); + + std::u32string val; + val.reserve(len); + + for (int j = 0; j < len; ++j) + { + repeat: + auto c = cgen(mt); + if (c >= cp::surrogate_min && c <= cp::surrogate_max) + goto repeat; + val.push_back(c); + } + result.emplace_back(cp::convert(val)); + } + return result; +} +} // anonymous namespace + + +#include "../src/private/strings-private.hpp" + +#include "fixture_init.hpp" + +namespace svn = ::apache::subversion::svnxx; +namespace detail = ::apache::subversion::svnxx::detail; + +BOOST_AUTO_TEST_SUITE(strings, + * boost::unit_test::fixture<init>()); + +BOOST_AUTO_TEST_CASE(wstring_conversion_roundtrip) +{ + for (const auto& sample : generate_string_data<wchar_t>(100)) + BOOST_TEST((sample == detail::convert<wchar_t>(detail::convert(sample)))); +} + +BOOST_AUTO_TEST_CASE(u16string_conversion_roundtrip) +{ + for (const auto& sample : generate_string_data<char16_t>(100)) + BOOST_TEST((sample == detail::convert<char16_t>(detail::convert(sample)))); +} + +BOOST_AUTO_TEST_CASE(u32string_conversion_roundtrip) +{ + for (const auto& sample : generate_string_data<char32_t>(100)) + BOOST_TEST((sample == detail::convert<char32_t>(detail::convert(sample)))); +} + +BOOST_AUTO_TEST_CASE(nulchar) +{ + const std::string nulstr("\0", 1); + const std::wstring wnulstr(L"\0", 1); + const std::u16string u16nulstr(u"\0", 1); + const std::u32string u32nulstr(U"\0", 1); + + BOOST_TEST(nulstr.size() == 1); + BOOST_TEST(wnulstr.size() == 1); + BOOST_TEST(u16nulstr.size() == 1); + BOOST_TEST(u32nulstr.size() == 1); + + BOOST_TEST(detail::convert<wchar_t>(nulstr).size() == 1); + BOOST_TEST(detail::convert<char16_t>(nulstr).size() == 1); + BOOST_TEST(detail::convert<char32_t>(nulstr).size() == 1); + + BOOST_TEST((detail::convert<wchar_t>(nulstr) == wnulstr)); + BOOST_TEST((detail::convert<char16_t>(nulstr) == u16nulstr)); + BOOST_TEST((detail::convert<char32_t>(nulstr) == u32nulstr)); + + BOOST_TEST(detail::convert(wnulstr).size() == 1); + BOOST_TEST(detail::convert(u16nulstr).size() == 1); + BOOST_TEST(detail::convert(u32nulstr).size() == 1); + + BOOST_TEST((detail::convert(wnulstr) == nulstr)); + BOOST_TEST((detail::convert(u16nulstr) == nulstr)); + BOOST_TEST((detail::convert(u32nulstr) == nulstr)); +} + +BOOST_AUTO_TEST_SUITE_END(); Propchange: subversion/trunk/subversion/bindings/cxx/tests/test_strings.cpp ------------------------------------------------------------------------------ svn:eol-style = native