419 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: c++03, c++11, c++1410 11// These tests require locale for non-char paths12// UNSUPPORTED: no-localization13 14// In MinGW mode, with optimizations enabled with a DLL, the number of counted15// allocations mismatches, as some ctor/dtor calls are generated in the16// calling code, and some are called from the DLL.17// ADDITIONAL_COMPILE_FLAGS: -DALLOW_MISMATCHING_LIBRRARY_INTERNAL_ALLOCATIONS18 19// <filesystem>20 21// class path22 23// path& operator+=(const path& x);24// path& operator+=(const string_type& x);25// path& operator+=(string_view x);26// path& operator+=(const value_type* x);27// path& operator+=(value_type x);28// template <class Source>29// path& operator+=(const Source& x);30// template <class EcharT>31// path& operator+=(EcharT x);32// template <class Source>33// path& concat(const Source& x);34// template <class InputIterator>35// path& concat(InputIterator first, InputIterator last);36 37#include <filesystem>38#include <type_traits>39#include <string>40#include <string_view>41#include <cassert>42#include <utility>43 44// On Windows, charset conversions cause allocations in the path class in45// cases where no allocations are done on other platforms.46 47#include "../path_helper.h"48#include "count_new.h"49#include "make_string.h"50#include "test_iterators.h"51#include "test_macros.h"52namespace fs = std::filesystem;53 54struct ConcatOperatorTestcase {55 MultiStringType lhs;56 MultiStringType rhs;57 MultiStringType expect;58};59 60#define LONGSTR "LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR_LONGSTR"61#define S(Str) MKSTR(Str)62const ConcatOperatorTestcase Cases[] =63 {64 {S(""), S(""), S("")}65 , {S("p1"), S("p2"), S("p1p2")}66 , {S("p1/"), S("/p2"), S("p1//p2")}67 , {S(""), S("\\foo/bar/baz"), S("\\foo/bar/baz")}68 , {S("c:\\foo"), S(""), S("c:\\foo")}69 , {S(LONGSTR), S("foo"), S(LONGSTR "foo")}70 , {S("abcdefghijklmnopqrstuvwxyz/\\"), S("/\\123456789"), S("abcdefghijklmnopqrstuvwxyz/\\/\\123456789")}71 };72const ConcatOperatorTestcase LongLHSCases[] =73 {74 {S(""), S(LONGSTR), S(LONGSTR)}75 , {S("p1/"), S(LONGSTR), S("p1/" LONGSTR)}76 };77const ConcatOperatorTestcase CharTestCases[] =78 {79 {S(""), S("P"), S("P")}80 , {S("/fooba"), S("r"), S("/foobar")}81 };82#undef S83#undef LONGSTR84 85// The concat operator may need to allocate a temporary buffer before a code_cvt86// conversion. Test if this allocation occurs by:87// 1. Create a path, `LHS`, and reserve enough space to append `RHS`.88// This prevents `LHS` from allocating during the actual appending.89// 2. Create a `Source` object `RHS`, which represents a "large" string.90// (The string must not trigger the SSO)91// 3. Concat `RHS` to `LHS` and check for the expected allocation behavior.92template <class CharT>93void doConcatSourceAllocTest(ConcatOperatorTestcase const& TC)94{95 using namespace fs;96 using Ptr = CharT const*;97 using Str = std::basic_string<CharT>;98 using StrView = std::basic_string_view<CharT>;99 using InputIter = cpp17_input_iterator<Ptr>;100 101 const Ptr L = TC.lhs;102 const Ptr R = TC.rhs;103 const Ptr E = TC.expect;104 std::size_t ReserveSize = StrLen(E) + 1;105 // basic_string106 {107 path LHS(L); PathReserve(LHS, ReserveSize);108 Str RHS(R);109 {110 TEST_NOT_WIN32(DisableAllocationGuard g);111 LHS += RHS;112 }113 assert(LHS == E);114 }115 // basic_string_view116 {117 path LHS(L); PathReserve(LHS, ReserveSize);118 StrView RHS(R);119 {120 TEST_NOT_WIN32(DisableAllocationGuard g);121 LHS += RHS;122 }123 assert(LHS == E);124 }125 // CharT*126 {127 path LHS(L); PathReserve(LHS, ReserveSize);128 Ptr RHS(R);129 {130 TEST_NOT_WIN32(DisableAllocationGuard g);131 LHS += RHS;132 }133 assert(LHS == E);134 }135 {136 path LHS(L); PathReserve(LHS, ReserveSize);137 Ptr RHS(R);138 {139 TEST_NOT_WIN32(DisableAllocationGuard g);140 LHS.concat(RHS, StrEnd(RHS));141 }142 assert(LHS == E);143 }144 // input iterator - For non-native char types, appends needs to copy the145 // iterator range into a contiguous block of memory before it can perform the146 // code_cvt conversions.147 // For the path native type, no allocations will be performed because no148 // conversion is required.149 150#if TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS151 // Only check allocations if we can pick up allocations done within the152 // library implementation.153 bool ExpectNoAllocations = std::is_same<CharT, path::value_type>::value;154#endif155 {156 path LHS(L); PathReserve(LHS, ReserveSize);157 InputIter RHS(R);158 {159 RequireAllocationGuard g(0); // require "at least zero" allocations by default160#if TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS161 if (ExpectNoAllocations)162 g.requireExactly(0);163#endif164 LHS += RHS;165 }166 assert(LHS == E);167 }168 {169 path LHS(L); PathReserve(LHS, ReserveSize);170 InputIter RHS(R);171 InputIter REnd(StrEnd(R));172 {173 RequireAllocationGuard g(0); // require "at least zero" allocations by default174#if TEST_SUPPORTS_LIBRARY_INTERNAL_ALLOCATIONS175 if (ExpectNoAllocations)176 g.requireExactly(0);177#endif178 LHS.concat(RHS, REnd);179 }180 assert(LHS == E);181 }182}183 184template <class CharT>185void doConcatSourceTest(ConcatOperatorTestcase const& TC)186{187 using namespace fs;188 using Ptr = CharT const*;189 using Str = std::basic_string<CharT>;190 using StrView = std::basic_string_view<CharT>;191 using InputIter = cpp17_input_iterator<Ptr>;192 const Ptr L = TC.lhs;193 const Ptr R = TC.rhs;194 const Ptr E = TC.expect;195 // basic_string196 {197 path LHS(L);198 Str RHS(R);199 path& Ref = (LHS += RHS);200 assert(LHS == E);201 assert(&Ref == &LHS);202 }203 {204 path LHS(L);205 Str RHS(R);206 path& Ref = LHS.concat(RHS);207 assert(LHS == E);208 assert(&Ref == &LHS);209 }210 // basic_string_view211 {212 path LHS(L);213 StrView RHS(R);214 path& Ref = (LHS += RHS);215 assert(LHS == E);216 assert(&Ref == &LHS);217 }218 {219 path LHS(L);220 StrView RHS(R);221 path& Ref = LHS.concat(RHS);222 assert(LHS == E);223 assert(&Ref == &LHS);224 }225 // Char*226 {227 path LHS(L);228 Str RHS(R);229 path& Ref = (LHS += RHS);230 assert(LHS == E);231 assert(&Ref == &LHS);232 }233 {234 path LHS(L);235 Ptr RHS(R);236 path& Ref = LHS.concat(RHS);237 assert(LHS == E);238 assert(&Ref == &LHS);239 }240 {241 path LHS(L);242 Ptr RHS(R);243 path& Ref = LHS.concat(RHS, StrEnd(RHS));244 assert(LHS == E);245 assert(&Ref == &LHS);246 }247 // iterators248 {249 path LHS(L);250 InputIter RHS(R);251 path& Ref = (LHS += RHS);252 assert(LHS == E);253 assert(&Ref == &LHS);254 }255 {256 path LHS(L); InputIter RHS(R);257 path& Ref = LHS.concat(RHS);258 assert(LHS == E);259 assert(&Ref == &LHS);260 }261 {262 path LHS(L);263 InputIter RHS(R);264 InputIter REnd(StrEnd(R));265 path& Ref = LHS.concat(RHS, REnd);266 assert(LHS == E);267 assert(&Ref == &LHS);268 }269}270 271template <class CharT>272void doConcatECharTest(ConcatOperatorTestcase const& TC)273{274 using namespace fs;275 using Ptr = CharT const*;276 const Ptr RStr = TC.rhs;277 assert(StrLen(RStr) == 1);278 const Ptr L = TC.lhs;279 const CharT R = RStr[0];280 const Ptr E = TC.expect;281 {282 path LHS(L);283 path& Ref = (LHS += R);284 assert(LHS == E);285 assert(&Ref == &LHS);286 }287}288 289 290template <class It, class = decltype(fs::path{}.concat(std::declval<It>()))>291constexpr bool has_concat(int) { return true; }292template <class It>293constexpr bool has_concat(long) { return false; }294 295template <class It, class = decltype(fs::path{}.operator+=(std::declval<It>()))>296constexpr bool has_concat_op(int) { return true; }297template <class It>298constexpr bool has_concat_op(long) { return false; }299template <class It>300constexpr bool has_concat_op() { return has_concat_op<It>(0); }301 302template <class It>303constexpr bool has_concat() {304 static_assert(has_concat<It>(0) == has_concat_op<It>(0), "must be same");305 return has_concat<It>(0) && has_concat_op<It>(0);306}307 308void test_sfinae() {309 using namespace fs;310 {311 static_assert(has_concat_op<char>(), "");312 static_assert(has_concat_op<const char>(), "");313 static_assert(has_concat_op<char16_t>(), "");314 static_assert(has_concat_op<const char16_t>(), "");315 }316 {317 using It = const char* const;318 static_assert(has_concat<It>(), "");319 }320 {321 using It = cpp17_input_iterator<const char*>;322 static_assert(has_concat<It>(), "");323 }324 {325 struct Traits {326 using iterator_category = std::input_iterator_tag;327 using value_type = const char;328 using pointer = const char*;329 using reference = const char&;330 using difference_type = std::ptrdiff_t;331 };332 using It = cpp17_input_iterator<const char*, Traits>;333 static_assert(has_concat<It>(), "");334 }335 {336 using It = cpp17_output_iterator<const char*>;337 static_assert(!has_concat<It>(), "");338 }339 {340 static_assert(!has_concat<int>(0), "");341 // operator+=(int) is well formed since it converts to operator+=(value_type)342 // but concat(int) isn't valid because there is no concat(value_type).343 // This should probably be addressed by a LWG issue.344 static_assert(has_concat_op<int>(), "");345 }346 {347 static_assert(!has_concat<int*>(), "");348 }349}350 351int main(int, char**)352{353 using namespace fs;354 for (auto const & TC : Cases) {355 {356 path LHS((const char*)TC.lhs);357 path RHS((const char*)TC.rhs);358 path& Ref = (LHS += RHS);359 assert(LHS == (const char*)TC.expect);360 assert(&Ref == &LHS);361 }362 {363 path LHS((const char*)TC.lhs);364 std::basic_string_view<path::value_type> RHS((const path::value_type*)TC.rhs);365 path& Ref = (LHS += RHS);366 assert(LHS == (const char*)TC.expect);367 assert(&Ref == &LHS);368 }369 doConcatSourceTest<char> (TC);370#ifndef TEST_HAS_NO_WIDE_CHARACTERS371 doConcatSourceTest<wchar_t> (TC);372#endif373 doConcatSourceTest<char16_t>(TC);374 doConcatSourceTest<char32_t>(TC);375 }376 for (auto const & TC : LongLHSCases) {377 // Do path test378 {379 path LHS((const char*)TC.lhs);380 path RHS((const char*)TC.rhs);381 const char* E = TC.expect;382 PathReserve(LHS, StrLen(E) + 5);383 {384 LIBCPP_ONLY(DisableAllocationGuard g);385 path& Ref = (LHS += RHS);386 assert(&Ref == &LHS);387 }388 assert(LHS == E);389 }390 {391 path LHS((const char*)TC.lhs);392 std::basic_string_view<path::value_type> RHS((const path::value_type*)TC.rhs);393 const char* E = TC.expect;394 PathReserve(LHS, StrLen(E) + 5);395 {396 LIBCPP_ONLY(DisableAllocationGuard g);397 path& Ref = (LHS += RHS);398 assert(&Ref == &LHS);399 }400 assert(LHS == E);401 }402 LIBCPP_ONLY(doConcatSourceAllocTest<char>(TC));403#ifndef TEST_HAS_NO_WIDE_CHARACTERS404 LIBCPP_ONLY(doConcatSourceAllocTest<wchar_t>(TC));405#endif406 }407 for (auto const& TC : CharTestCases) {408 doConcatECharTest<char>(TC);409#ifndef TEST_HAS_NO_WIDE_CHARACTERS410 doConcatECharTest<wchar_t>(TC);411#endif412 doConcatECharTest<char16_t>(TC);413 doConcatECharTest<char32_t>(TC);414 }415 test_sfinae();416 417 return 0;418}419