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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 // UNSUPPORTED: no-localization
11 // UNSUPPORTED: libcpp-has-no-experimental-syncstream
12 
13 // <syncstream>
14 
15 // template <class charT, class traits, class Allocator>
16 // class basic_osyncstream;
17 
18 // basic_osyncstream& operator=(basic_osyncstream&& rhs);
19 
20 #include <syncstream>
21 #include <sstream>
22 #include <cassert>
23 
24 #include "test_macros.h"
25 #include "test_allocator.h"
26 
27 template <class CharT, bool propagate>
test()28 static void test() {
29   using Traits    = std::char_traits<CharT>;
30   using Allocator = std::conditional_t<propagate, other_allocator<CharT>, test_allocator<CharT>>;
31   static_assert(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value == propagate);
32 
33   using OS = std::basic_osyncstream<CharT, Traits, Allocator>;
34 
35   std::basic_stringbuf<CharT, Traits, Allocator> base1;
36   std::basic_stringbuf<CharT, Traits, Allocator> base2;
37 
38   {
39     OS out1{&base1, Allocator{42}};
40     assert(out1.get_wrapped() == &base1);
41 
42     typename OS::syncbuf_type* sb1 = out1.rdbuf();
43     assert(sb1->get_wrapped() == &base1);
44     assert(sb1->get_allocator().get_data() == 42);
45 
46     out1 << CharT('A');
47 
48     static_assert(!noexcept(out1.operator=(std::move(out1)))); // LWG-3867
49     OS out2{&base2, Allocator{99}};
50 
51     out2 << CharT('Z');
52 
53     // Validate the data is still in the syncbuf and not in the stringbuf.
54     assert(base1.str().empty());
55     assert(base2.str().empty());
56 
57     out2 = std::move(out1);
58 
59     // Since sb2 is overwritten by the move its data should be in its stringbuf.
60     assert(base1.str().empty());
61     assert(base2.str().size() == 1);
62     assert(base2.str()[0] == CharT('Z'));
63 
64     assert(out2.get_wrapped() == &base1);
65 
66     typename OS::syncbuf_type* sb2 = out2.rdbuf();
67     assert(sb2->get_wrapped() == &base1);
68     if constexpr (std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value)
69       assert(sb2->get_allocator().get_data() == 42);
70     else
71       assert(sb2->get_allocator().get_data() == 99);
72 
73     assert(out1.get_wrapped() == nullptr);
74     assert(sb1->get_wrapped() == nullptr);
75 
76     // The data written to 2 will be stored in sb1. The write happens after the destruction.
77     out2 << CharT('B');
78     assert(base1.str().empty());
79   }
80 
81   assert(base1.str().size() == 2);
82   assert(base1.str()[0] == CharT('A'));
83   assert(base1.str()[1] == CharT('B'));
84   assert(base2.str().size() == 1);
85   assert(base2.str()[0] == CharT('Z'));
86 }
87 
88 template <class CharT>
test()89 static void test() {
90   test<CharT, true>();
91   test<CharT, false>();
92 }
93 
main(int,char **)94 int main(int, char**) {
95   test<char>();
96 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
97   test<wchar_t>();
98 #endif
99 
100   return 0;
101 }
102