brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.1 KiB · ba5aff4 Raw
415 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: no-localization10// UNSUPPORTED: c++03, c++11, c++1411// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS12 13// <filesystem>14 15// class path16 17// Test constructors, accessors and modifiers that convert from/to various18// character encodings. Constructors and modifiers (append, concat,19// operator/=, operator+=) accept inputs with various character encodings,20// and accessors (*string(), string<>(), u8string()) export the string with21// various encodings.22//23// Some encodings are standardized; char16_t, char32_t and the u8string24// accessor and u8path constructor (and normal functions taking char8_t in25// C++20) convert from/to UTF-16, UTF-32 and UTF-8. wchar_t can be either26// UTF-16 or UTF-32 depending on the size of the wchar_t type, or can be27// left unimplemented.28//29// Plain char is implicitly UTF-8 on posix systems. On Windows, plain char30// is supposed to be in the same encoding as the platform's native file31// system APIs consumes in the functions that take narrow strings as path32// names.33 34#include <filesystem>35#include <type_traits>36#include <cassert>37 38#include "test_macros.h"39 40#ifdef _WIN3241#  include <windows.h> // SetFileApisToANSI & friends42#endif43namespace fs = std::filesystem;44 45// Test conversion with strings that fit within the latin1 charset, that fit46// within one code point in UTF-16, and that can be expressible in certain47// one-byte code pages.48static void test_latin_unicode()49{50  const char16_t u16str[] = { 0xe5, 0xe4, 0xf6, 0x00 };51  const char32_t u32str[] = { 0xe5, 0xe4, 0xf6, 0x00 };52  const char str[] = { char(0xc3), char(0xa5), char(0xc3), char(0xa4), char(0xc3), char(0xb6), 0x00 }; // UTF8, in a regular char string53#if TEST_STD_VER > 17 && defined(__cpp_lib_char8_t)54  const char8_t u8str[] = { 0xc3, 0xa5, 0xc3, 0xa4, 0xc3, 0xb6, 0x00 };55#else56  const char u8str[] = { char(0xc3), char(0xa5), char(0xc3), char(0xa4), char(0xc3), char(0xb6), 0x00 };57#endif58#ifndef TEST_HAS_NO_WIDE_CHARACTERS59  const wchar_t wstr[] = { 0xe5, 0xe4, 0xf6, 0x00 };60#endif61 62  // Test well-defined conversion between UTF-8, UTF-16 and UTF-3263  {64    const fs::path p(u16str);65    assert(p.u8string() == u8str);66    assert(p.u16string() == u16str);67    assert(p.u32string() == u32str);68    assert(p.string<char16_t>() == u16str);69    assert(p.string<char32_t>() == u32str);70  }71  {72    const fs::path p(u32str);73    assert(p.u8string() == u8str);74    assert(p.u16string() == u16str);75    assert(p.u32string() == u32str);76    assert(p.string<char16_t>() == u16str);77    assert(p.string<char32_t>() == u32str);78  }79  {80    const fs::path p = fs::u8path(str);81    assert(p.u8string() == u8str);82    assert(p.u16string() == u16str);83    assert(p.u32string() == u32str);84    assert(p.string<char16_t>() == u16str);85    assert(p.string<char32_t>() == u32str);86  }87#if TEST_STD_VER > 17 && defined(__cpp_lib_char8_t)88  {89    // In C++20, the path constructor can unambiguously handle UTF-8 input,90    // even if the plain char constructor would treat it as something else.91    const fs::path p(u8str);92    assert(p.u8string() == u8str);93    assert(p.u16string() == u16str);94    assert(p.u32string() == u32str);95    assert(p.string<char8_t>() == u8str);96    assert(p.string<char16_t>() == u16str);97    assert(p.string<char32_t>() == u32str);98  }99  // Check reading various inputs with string<char8_t>()100  {101    const fs::path p(u16str);102    assert(p.string<char8_t>() == u8str);103  }104  {105    const fs::path p(u32str);106    assert(p.string<char8_t>() == u8str);107  }108  {109    const fs::path p = fs::u8path(str);110    assert(p.string<char8_t>() == u8str);111  }112#endif113#ifndef TEST_HAS_NO_WIDE_CHARACTERS114  // Test conversion to/from wchar_t.115  {116    const fs::path p(u16str);117    assert(p.wstring() == wstr);118    assert(p.string<wchar_t>() == wstr);119  }120  {121    const fs::path p = fs::u8path(str);122    assert(p.wstring() == wstr);123    assert(p.string<wchar_t>() == wstr);124  }125  {126    const fs::path p(wstr);127    assert(p.wstring() == wstr);128    assert(p.u8string() == u8str);129    assert(p.u16string() == u16str);130    assert(p.u32string() == u32str);131    assert(p.string<wchar_t>() == wstr);132  }133#endif // TEST_HAS_NO_WIDE_CHARACTERS134#ifndef _WIN32135  // Test conversion to/from regular char-based string. On POSIX, this136  // is implied to convert to/from UTF-8.137  {138    const fs::path p(str);139    assert(p.string() == str);140    assert(p.u16string() == u16str);141    assert(p.string<char>() == str);142  }143  {144    const fs::path p(u16str);145    assert(p.string() == str);146    assert(p.string<char>() == str);147  }148#else149  // On windows, the narrow char-based input/output is supposed to be150  // in the charset that narrow file IO APIs use. This can either be the151  // current active code page (ACP) or the OEM code page, exposed by152  // the AreFileApisANSI() function, and settable with SetFileApisToANSI() and153  // SetFileApisToOEM(). We can't set which codepage is active within154  // the process, but for some specific known ones, we can check if they155  // behave as expected.156  SetFileApisToANSI();157  if (GetACP() == 1252) {158    const char latin1[] = { char(0xe5), char(0xe4), char(0xf6), 0x00 };159    {160      const fs::path p(wstr);161      assert(p.string() == latin1);162      assert(p.string<char>() == latin1);163    }164    {165      const fs::path p(latin1);166      assert(p.string() == latin1);167      assert(p.wstring() == wstr);168      assert(p.u8string() == u8str);169      assert(p.u16string() == u16str);170      assert(p.string<char>() == latin1);171      assert(p.string<wchar_t>() == wstr);172    }173  }174  SetFileApisToOEM();175  if (GetOEMCP() == 850 || GetOEMCP() == 437) {176    // These chars are identical in both CP 850 and 437177    const char cp850[] = { char(0x86), char(0x84), char(0x94), 0x00 };178    {179      const fs::path p(wstr);180      assert(p.string() == cp850);181      assert(p.string<char>() == cp850);182    }183    {184      const fs::path p(cp850);185      assert(p.string() == cp850);186      assert(p.wstring() == wstr);187      assert(p.u8string() == u8str);188      assert(p.u16string() == u16str);189      assert(p.string<char>() == cp850);190      assert(p.string<wchar_t>() == wstr);191    }192  }193#endif194}195 196// Test conversion with strings that don't fit within one UTF-16 code point.197// Here, wchar_t can be either UTF-16 or UTF-32 depending on the size on the198// particular platform.199static void test_wide_unicode()200{201  const char16_t u16str[] = { 0xd801, 0xdc37, 0x00 };202  const char32_t u32str[] = { 0x10437, 0x00 };203#if TEST_STD_VER > 17 && defined(__cpp_lib_char8_t)204  const char8_t u8str[] = { 0xf0, 0x90, 0x90, 0xb7, 0x00 };205#else206  const char u8str[] = { char(0xf0), char(0x90), char(0x90), char(0xb7), 0x00 };207#endif208  const char str[] = { char(0xf0), char(0x90), char(0x90), char(0xb7), 0x00 };209  {210    const fs::path p = fs::u8path(str);211    assert(p.u8string() == u8str);212    assert(p.u16string() == u16str);213    assert(p.u32string() == u32str);214  }215  {216    const fs::path p(u16str);217    assert(p.u8string() == u8str);218    assert(p.u16string() == u16str);219    assert(p.u32string() == u32str);220  }221  {222    const fs::path p(u32str);223    assert(p.u8string() == u8str);224    assert(p.u16string() == u16str);225    assert(p.u32string() == u32str);226  }227#if !defined(TEST_HAS_NO_WIDE_CHARACTERS) && defined(__SIZEOF_WCHAR_T__)228# if __SIZEOF_WCHAR_T__ == 2229  const wchar_t wstr[] = { 0xd801, 0xdc37, 0x00 };230# else231  const wchar_t wstr[] = { 0x10437, 0x00 };232# endif233  // Test conversion to/from wchar_t.234  {235    const fs::path p = fs::u8path(str);236    assert(p.wstring() == wstr);237  }238  {239    const fs::path p(u16str);240    assert(p.wstring() == wstr);241  }242  {243    const fs::path p(u32str);244    assert(p.wstring() == wstr);245  }246  {247    const fs::path p(wstr);248    assert(p.u8string() == u8str);249    assert(p.u16string() == u16str);250    assert(p.u32string() == u32str);251    assert(p.wstring() == wstr);252  }253#endif // !defined(TEST_HAS_NO_WIDE_CHARACTERS) && defined(__SIZEOF_WCHAR_T__)254}255 256// Test appending paths in different encodings.257static void test_append()258{259  const char16_t u16str[] = { 0xd801, 0xdc37, 0x00 };260  const char32_t u32str[] = { 0x10437, 0x00 };261  const char32_t u32ref[] = { 0x10437, fs::path::preferred_separator, 0x10437, fs::path::preferred_separator, 0x10437, 0x00 };262  const char str[] = { char(0xf0), char(0x90), char(0x90), char(0xb7), 0x00 };263  {264    fs::path p = fs::u8path(str) / u16str / u32str;265    assert(p.u32string() == u32ref);266    p = fs::u8path(str).append(u16str).append(u32str);267    assert(p.u32string() == u32ref);268    p = fs::u8path(str);269    p /= u16str;270    p /= u32str;271    assert(p.u32string() == u32ref);272  }273#if !defined(TEST_HAS_NO_WIDE_CHARACTERS) && defined(__SIZEOF_WCHAR_T__)274# if __SIZEOF_WCHAR_T__ == 2275  const wchar_t wstr[] = { 0xd801, 0xdc37, 0x00 };276# else277  const wchar_t wstr[] = { 0x10437, 0x00 };278# endif279  // Test conversion from wchar_t.280  {281    fs::path p = fs::path(u16str) / wstr / u32str;282    assert(p.u32string() == u32ref);283    p = fs::path(u16str).append(wstr).append(u32str);284    assert(p.u32string() == u32ref);285    p = fs::path(u16str);286    p /= wstr;287    p /= u32str;288    assert(p.u32string() == u32ref);289  }290#endif // !defined(TEST_HAS_NO_WIDE_CHARACTERS) && defined(__SIZEOF_WCHAR_T__)291}292 293static void test_concat()294{295  const char16_t u16str[] = { 0xd801, 0xdc37, 0x00 };296  const char32_t u32str[] = { 0x10437, 0x00 };297  const char32_t u32ref[] = { 0x10437, 0x10437, 0x10437, 0x00 };298  const char str[] = { char(0xf0), char(0x90), char(0x90), char(0xb7), 0x00 };299  {300    fs::path p = fs::u8path(str);301    p += u16str;302    p += u32str;303    assert(p.u32string() == u32ref);304    p = fs::u8path(str).concat(u16str).concat(u32str);305    assert(p.u32string() == u32ref);306  }307#if !defined(TEST_HAS_NO_WIDE_CHARACTERS) && defined(__SIZEOF_WCHAR_T__)308# if __SIZEOF_WCHAR_T__ == 2309  const wchar_t wstr[] = { 0xd801, 0xdc37, 0x00 };310# else311  const wchar_t wstr[] = { 0x10437, 0x00 };312# endif313  // Test conversion from wchar_t.314  {315    fs::path p = fs::path(u16str);316    p += wstr;317    p += u32str;318    assert(p.u32string() == u32ref);319    p = fs::path(u16str).concat(wstr).concat(u32str);320    assert(p.u32string() == u32ref);321  }322#endif // !defined(TEST_HAS_NO_WIDE_CHARACTERS) && defined(__SIZEOF_WCHAR_T__)323}324 325static void test_append_concat_narrow()326{327  const char16_t u16str[] = { 0xe5, 0x00 };328  const char32_t u32ref_append[] = { 0xe5, fs::path::preferred_separator, 0xe5, 0x00 };329  const char32_t u32ref_concat[] = { 0xe5, 0xe5, 0x00 };330 331#if TEST_STD_VER > 17 && defined(__cpp_lib_char8_t)332  {333    const char8_t u8str[] = { 0xc3, 0xa5, 0x00 };334    // In C++20, appends of a char8_t string is unambiguously treated as335    // UTF-8.336    fs::path p = fs::path(u16str) / u8str;337    assert(p.u32string() == u32ref_append);338    p = fs::path(u16str).append(u8str);339    assert(p.u32string() == u32ref_append);340    p = fs::path(u16str);341    p /= u8str;342    assert(p.u32string() == u32ref_append);343    p = fs::path(u16str).concat(u8str);344    assert(p.u32string() == u32ref_concat);345    p = fs::path(u16str);346    p += u8str;347    assert(p.u32string() == u32ref_concat);348  }349#endif350#ifndef _WIN32351  // Test appending a regular char-based string. On POSIX, this352  // is implied to convert to/from UTF-8.353  {354    const char str[] = { char(0xc3), char(0xa5), 0x00 }; // UTF8, in a regular char string355    fs::path p = fs::path(u16str) / str;356    assert(p.u32string() == u32ref_append);357    p = fs::path(u16str).append(str);358    assert(p.u32string() == u32ref_append);359    p = fs::path(u16str);360    p /= str;361    assert(p.u32string() == u32ref_append);362    p = fs::path(u16str).concat(str);363    assert(p.u32string() == u32ref_concat);364    p = fs::path(u16str);365    p += str;366    assert(p.u32string() == u32ref_concat);367  }368#else369  SetFileApisToANSI();370  if (GetACP() == 1252) {371    const char latin1[] = { char(0xe5), 0x00 };372    fs::path p = fs::path(u16str) / latin1;373    assert(p.u32string() == u32ref_append);374    p = fs::path(u16str).append(latin1);375    assert(p.u32string() == u32ref_append);376    p = fs::path(u16str);377    p /= latin1;378    assert(p.u32string() == u32ref_append);379    p = fs::path(u16str).concat(latin1);380    assert(p.u32string() == u32ref_concat);381    p = fs::path(u16str);382    p += latin1;383    assert(p.u32string() == u32ref_concat);384  }385  SetFileApisToOEM();386  if (GetOEMCP() == 850 || GetOEMCP() == 437) {387    // This chars is identical in both CP 850 and 437388    const char cp850[] = { char(0x86), 0x00 };389    fs::path p = fs::path(u16str) / cp850;390    assert(p.u32string() == u32ref_append);391    p = fs::path(u16str).append(cp850);392    assert(p.u32string() == u32ref_append);393    p = fs::path(u16str);394    p /= cp850;395    assert(p.u32string() == u32ref_append);396    p = fs::path(u16str).concat(cp850);397    assert(p.u32string() == u32ref_concat);398    p = fs::path(u16str);399    p += cp850;400    assert(p.u32string() == u32ref_concat);401  }402#endif403}404 405int main(int, char**)406{407  test_latin_unicode();408  test_wide_unicode();409  test_append();410  test_concat();411  test_append_concat_narrow();412 413  return 0;414}415