1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <folly/Replaceable.h>
18 
19 #include <folly/portability/GTest.h>
20 
21 using namespace ::testing;
22 using namespace ::folly;
23 
24 namespace {
25 struct Basic {};
26 struct alignas(128) BigAlign {};
27 struct HasConst final {
28   bool const b1;
HasConst__anon1b2344a00111::HasConst29   HasConst() noexcept : b1(true) {}
HasConst__anon1b2344a00111::HasConst30   explicit HasConst(bool b) noexcept : b1(b) {}
HasConst__anon1b2344a00111::HasConst31   HasConst(HasConst const& b) noexcept : b1(b.b1) {}
HasConst__anon1b2344a00111::HasConst32   HasConst(HasConst&& b) noexcept : b1(b.b1) {}
33   HasConst& operator=(HasConst const&) = delete;
34   HasConst& operator=(HasConst&&) = delete;
35 };
36 struct HasRef final {
37   int& i1;
HasRef__anon1b2344a00111::HasRef38   explicit HasRef(int& i) noexcept(false) : i1(i) {}
HasRef__anon1b2344a00111::HasRef39   HasRef(HasRef const& i) noexcept(false) : i1(i.i1) {}
HasRef__anon1b2344a00111::HasRef40   HasRef(HasRef&& i) noexcept(false) : i1(i.i1) {}
41   HasRef& operator=(HasRef const&) = delete;
42   HasRef& operator=(HasRef&&) = delete;
~HasRef__anon1b2344a00111::HasRef43   ~HasRef() noexcept(false) { ++i1; }
44 };
swap(HasRef & lhs,HasRef & rhs)45 void swap(HasRef& lhs, HasRef& rhs) noexcept(false) {
46   std::swap(lhs.i1, rhs.i1);
47 }
48 struct OddA;
49 struct OddB {
50   OddB() = delete;
OddB__anon1b2344a00111::OddB51   OddB(std::initializer_list<int>, int) noexcept(false) {}
OddB__anon1b2344a00111::OddB52   explicit OddB(OddA&&) {}
OddB__anon1b2344a00111::OddB53   explicit OddB(OddA const&) noexcept(false) {}
54   OddB(OddB&&) = delete;
55   OddB(OddB const&) = delete;
56   OddB& operator=(OddB&&) = delete;
57   OddB& operator=(OddB const&) = delete;
58   ~OddB() = default;
59 };
60 struct OddA {
61   OddA() = delete;
OddA__anon1b2344a00111::OddA62   explicit OddA(OddB&&) noexcept {}
63   explicit OddA(OddB const&) = delete;
64   OddA(OddA&&) = delete;
65   OddA(OddA const&) = delete;
66   OddA& operator=(OddA&&) = delete;
67   OddA& operator=(OddA const&) = delete;
~OddA__anon1b2344a00111::OddA68   ~OddA() noexcept(false) {}
69 };
70 struct Indestructible {
71   ~Indestructible() = delete;
72 };
73 
74 struct HasInt {
HasInt__anon1b2344a00111::HasInt75   explicit HasInt(int v) : value{v} {}
76   int value{};
77 };
78 } // namespace
79 
80 template <typename T>
81 struct ReplaceableStaticAttributeTest : Test {};
82 using StaticAttributeTypes = ::testing::Types<
83     char,
84     short,
85     int,
86     long,
87     float,
88     double,
89     char[11],
90     Basic,
91     BigAlign,
92     HasConst,
93     HasRef,
94     OddA,
95     OddB,
96     Indestructible>;
97 TYPED_TEST_SUITE(ReplaceableStaticAttributeTest, StaticAttributeTypes);
98 
99 template <typename T>
100 struct ReplaceableStaticAttributePairTest : Test {};
101 using StaticAttributePairTypes = ::testing::
102     Types<std::pair<int, long>, std::pair<OddA, OddB>, std::pair<OddB, OddA>>;
103 TYPED_TEST_SUITE(ReplaceableStaticAttributePairTest, StaticAttributePairTypes);
104 
TYPED_TEST(ReplaceableStaticAttributeTest,size)105 TYPED_TEST(ReplaceableStaticAttributeTest, size) {
106   EXPECT_EQ(sizeof(TypeParam), sizeof(Replaceable<TypeParam>));
107 }
TYPED_TEST(ReplaceableStaticAttributeTest,align)108 TYPED_TEST(ReplaceableStaticAttributeTest, align) {
109   EXPECT_EQ(alignof(TypeParam), alignof(Replaceable<TypeParam>));
110 }
TYPED_TEST(ReplaceableStaticAttributeTest,destructible)111 TYPED_TEST(ReplaceableStaticAttributeTest, destructible) {
112   EXPECT_EQ(
113       std::is_destructible<TypeParam>::value,
114       std::is_destructible<Replaceable<TypeParam>>::value);
115 }
TYPED_TEST(ReplaceableStaticAttributeTest,trivially_destructible)116 TYPED_TEST(ReplaceableStaticAttributeTest, trivially_destructible) {
117   EXPECT_EQ(
118       std::is_trivially_destructible<TypeParam>::value,
119       std::is_trivially_destructible<Replaceable<TypeParam>>::value);
120 }
TYPED_TEST(ReplaceableStaticAttributeTest,default_constructible)121 TYPED_TEST(ReplaceableStaticAttributeTest, default_constructible) {
122   EXPECT_EQ(
123       std::is_default_constructible<TypeParam>::value,
124       std::is_default_constructible<Replaceable<TypeParam>>::value);
125 }
TYPED_TEST(ReplaceableStaticAttributeTest,move_constructible)126 TYPED_TEST(ReplaceableStaticAttributeTest, move_constructible) {
127   EXPECT_EQ(
128       std::is_move_constructible<TypeParam>::value,
129       std::is_move_constructible<Replaceable<TypeParam>>::value);
130 }
TYPED_TEST(ReplaceableStaticAttributeTest,copy_constructible)131 TYPED_TEST(ReplaceableStaticAttributeTest, copy_constructible) {
132   EXPECT_EQ(
133       std::is_copy_constructible<TypeParam>::value,
134       std::is_copy_constructible<Replaceable<TypeParam>>::value);
135 }
TYPED_TEST(ReplaceableStaticAttributeTest,move_assignable)136 TYPED_TEST(ReplaceableStaticAttributeTest, move_assignable) {
137   EXPECT_EQ(
138       std::is_move_constructible<TypeParam>::value,
139       std::is_move_assignable<Replaceable<TypeParam>>::value);
140 }
TYPED_TEST(ReplaceableStaticAttributeTest,copy_assignable)141 TYPED_TEST(ReplaceableStaticAttributeTest, copy_assignable) {
142   EXPECT_EQ(
143       std::is_copy_constructible<TypeParam>::value,
144       std::is_copy_assignable<Replaceable<TypeParam>>::value);
145 }
TYPED_TEST(ReplaceableStaticAttributeTest,nothrow_destructible)146 TYPED_TEST(ReplaceableStaticAttributeTest, nothrow_destructible) {
147   EXPECT_EQ(
148       std::is_nothrow_destructible<TypeParam>::value,
149       std::is_nothrow_destructible<Replaceable<TypeParam>>::value);
150 }
TYPED_TEST(ReplaceableStaticAttributeTest,nothrow_default_constructible)151 TYPED_TEST(ReplaceableStaticAttributeTest, nothrow_default_constructible) {
152   EXPECT_EQ(
153       std::is_nothrow_default_constructible<TypeParam>::value,
154       std::is_nothrow_default_constructible<Replaceable<TypeParam>>::value);
155 }
TYPED_TEST(ReplaceableStaticAttributeTest,nothrow_move_constructible)156 TYPED_TEST(ReplaceableStaticAttributeTest, nothrow_move_constructible) {
157   EXPECT_EQ(
158       std::is_nothrow_move_constructible<TypeParam>::value,
159       std::is_nothrow_move_constructible<Replaceable<TypeParam>>::value);
160 }
TYPED_TEST(ReplaceableStaticAttributeTest,nothrow_copy_constructible)161 TYPED_TEST(ReplaceableStaticAttributeTest, nothrow_copy_constructible) {
162   EXPECT_EQ(
163       std::is_nothrow_copy_constructible<TypeParam>::value,
164       std::is_nothrow_copy_constructible<Replaceable<TypeParam>>::value);
165 }
TYPED_TEST(ReplaceableStaticAttributeTest,nothrow_move_assignable)166 TYPED_TEST(ReplaceableStaticAttributeTest, nothrow_move_assignable) {
167   EXPECT_EQ(
168       std::is_nothrow_destructible<TypeParam>::value &&
169           std::is_nothrow_copy_constructible<TypeParam>::value,
170       std::is_nothrow_move_assignable<Replaceable<TypeParam>>::value);
171 }
TYPED_TEST(ReplaceableStaticAttributeTest,nothrow_copy_assignable)172 TYPED_TEST(ReplaceableStaticAttributeTest, nothrow_copy_assignable) {
173   EXPECT_EQ(
174       std::is_nothrow_destructible<TypeParam>::value &&
175           std::is_nothrow_copy_constructible<TypeParam>::value,
176       std::is_nothrow_copy_assignable<Replaceable<TypeParam>>::value);
177 }
TYPED_TEST(ReplaceableStaticAttributeTest,replaceable)178 TYPED_TEST(ReplaceableStaticAttributeTest, replaceable) {
179   EXPECT_FALSE(is_replaceable<TypeParam>::value);
180   EXPECT_TRUE(is_replaceable<Replaceable<TypeParam>>::value);
181 }
182 
TYPED_TEST(ReplaceableStaticAttributePairTest,copy_construct)183 TYPED_TEST(ReplaceableStaticAttributePairTest, copy_construct) {
184   using T = typename TypeParam::first_type;
185   using U = typename TypeParam::second_type;
186   EXPECT_EQ(
187       (std::is_constructible<T, U const&>::value),
188       (std::is_constructible<Replaceable<T>, Replaceable<U> const&>::value));
189 }
TYPED_TEST(ReplaceableStaticAttributePairTest,move_construct)190 TYPED_TEST(ReplaceableStaticAttributePairTest, move_construct) {
191   using T = typename TypeParam::first_type;
192   using U = typename TypeParam::second_type;
193   EXPECT_EQ(
194       (std::is_constructible<T, U&&>::value),
195       (std::is_constructible<Replaceable<T>, Replaceable<U>&&>::value));
196 }
TYPED_TEST(ReplaceableStaticAttributePairTest,copy_assign)197 TYPED_TEST(ReplaceableStaticAttributePairTest, copy_assign) {
198   using T = typename TypeParam::first_type;
199   using U = typename TypeParam::second_type;
200   EXPECT_EQ(
201       (std::is_convertible<U, T>::value && std::is_destructible<T>::value &&
202        std::is_copy_constructible<T>::value),
203       (std::is_assignable<Replaceable<T>, Replaceable<U> const&>::value));
204 }
TYPED_TEST(ReplaceableStaticAttributePairTest,move_assign)205 TYPED_TEST(ReplaceableStaticAttributePairTest, move_assign) {
206   using T = typename TypeParam::first_type;
207   using U = typename TypeParam::second_type;
208   EXPECT_EQ(
209       (std::is_convertible<U, T>::value && std::is_destructible<T>::value &&
210        std::is_move_constructible<T>::value),
211       (std::is_assignable<Replaceable<T>, Replaceable<U>&&>::value));
212 }
TYPED_TEST(ReplaceableStaticAttributePairTest,nothrow_copy_construct)213 TYPED_TEST(ReplaceableStaticAttributePairTest, nothrow_copy_construct) {
214   using T = typename TypeParam::first_type;
215   using U = typename TypeParam::second_type;
216   EXPECT_EQ(
217       (std::is_nothrow_constructible<T, U const&>::value &&
218        std::is_nothrow_destructible<T>::value),
219       (std::is_nothrow_constructible<Replaceable<T>, Replaceable<U> const&>::
220            value));
221 }
TYPED_TEST(ReplaceableStaticAttributePairTest,nothrow_move_construct)222 TYPED_TEST(ReplaceableStaticAttributePairTest, nothrow_move_construct) {
223   using T = typename TypeParam::first_type;
224   using U = typename TypeParam::second_type;
225   EXPECT_EQ(
226       (std::is_nothrow_constructible<T, U&&>::value &&
227        std::is_nothrow_destructible<T>::value),
228       (std::is_nothrow_constructible<Replaceable<T>, Replaceable<U>&&>::value));
229 }
TYPED_TEST(ReplaceableStaticAttributePairTest,nothrow_copy_assign)230 TYPED_TEST(ReplaceableStaticAttributePairTest, nothrow_copy_assign) {
231   using T = typename TypeParam::first_type;
232   using U = typename TypeParam::second_type;
233   EXPECT_EQ(
234       (std::is_nothrow_constructible<T, U const&>::value &&
235        std::is_nothrow_destructible<T>::value),
236       (std::is_nothrow_assignable<Replaceable<T>, Replaceable<U> const&>::
237            value));
238 }
TYPED_TEST(ReplaceableStaticAttributePairTest,nothrow_move_assign)239 TYPED_TEST(ReplaceableStaticAttributePairTest, nothrow_move_assign) {
240   using T = typename TypeParam::first_type;
241   using U = typename TypeParam::second_type;
242   EXPECT_EQ(
243       (std::is_nothrow_constructible<T, U&&>::value &&
244        std::is_nothrow_destructible<T>::value),
245       (std::is_nothrow_assignable<Replaceable<T>, Replaceable<U>&&>::value));
246 }
247 
TEST(ReplaceableTest,Basics)248 TEST(ReplaceableTest, Basics) {
249   auto rHasConstA = make_replaceable<HasConst>();
250   auto rHasConstB = make_replaceable<HasConst>(false);
251   EXPECT_TRUE(rHasConstA->b1);
252   EXPECT_FALSE(rHasConstB->b1);
253   rHasConstA = rHasConstB;
254   EXPECT_FALSE(rHasConstA->b1);
255   EXPECT_FALSE(rHasConstB->b1);
256   rHasConstB.emplace(true);
257   EXPECT_FALSE(rHasConstA->b1);
258   EXPECT_TRUE(rHasConstB->b1);
259   rHasConstA = std::move(rHasConstB);
260   EXPECT_TRUE(rHasConstA->b1);
261   EXPECT_TRUE(rHasConstB->b1);
262 }
263 
TEST(ReplaceableTest,Constructors)264 TEST(ReplaceableTest, Constructors) {
265   Basic b{};
266   // From existing `T`
267   auto rBasicCopy1 = Replaceable<Basic>(b);
268   auto rBasicMove1 = Replaceable<Basic>(std::move(b));
269   // From existing `Replaceable<T>`
270   auto rBasicCopy2 = Replaceable<Basic>(rBasicCopy1);
271   auto rBasicMove2 = Replaceable<Basic>(std::move(rBasicMove1));
272   (void)rBasicCopy2;
273   (void)rBasicMove2;
274 }
275 
TEST(ReplaceableTest,DestructsWhenExpected)276 TEST(ReplaceableTest, DestructsWhenExpected) {
277   int i{0};
278   {
279     Replaceable<HasRef> rHasRefA{i};
280     Replaceable<HasRef> rHasRefB{i};
281     EXPECT_EQ(0, i);
282     rHasRefA = rHasRefB;
283     EXPECT_EQ(1, i);
284     rHasRefB.emplace(i);
285     EXPECT_EQ(2, i);
286     rHasRefA = std::move(rHasRefB);
287     EXPECT_EQ(3, i);
288   }
289   EXPECT_EQ(5, i);
290 }
291 
TEST(ReplaceableTest,Conversions)292 TEST(ReplaceableTest, Conversions) {
293   Replaceable<OddB> rOddB{in_place, {1, 2, 3}, 4};
294   Replaceable<OddA> rOddA{std::move(rOddB)};
295   Replaceable<OddB> rOddB2{rOddA};
296 }
297 
TEST(ReplaceableTest,swapMemberFunctionIsNoexcept)298 TEST(ReplaceableTest, swapMemberFunctionIsNoexcept) {
299   int v1{1};
300   int v2{2};
301   auto r1 = Replaceable<HasInt>{v1};
302   auto r2 = Replaceable<HasInt>{v2};
303   EXPECT_TRUE(noexcept(r1.swap(r2)));
304   r1.swap(r2);
305   EXPECT_EQ(v2, r1->value);
306   EXPECT_EQ(v1, r2->value);
307 }
308 
TEST(ReplaceableTest,swapMemberFunctionIsNotNoexcept)309 TEST(ReplaceableTest, swapMemberFunctionIsNotNoexcept) {
310   int v1{1};
311   int v2{2};
312   auto r1 = Replaceable<HasRef>{v1};
313   auto r2 = Replaceable<HasRef>{v2};
314   EXPECT_FALSE(noexcept(r1.swap(r2)));
315   r1.swap(r2);
316   EXPECT_EQ(v1, r1->i1);
317   EXPECT_EQ(v2, r2->i1);
318 }
319 
320 namespace adl_test {
321 struct UserDefinedSwap {
322   bool calledSwap{};
323 };
swap(UserDefinedSwap & lhs,UserDefinedSwap &)324 void swap(UserDefinedSwap& lhs, UserDefinedSwap&) noexcept(false) {
325   lhs.calledSwap = true;
326 }
327 } // namespace adl_test
328 
TEST(ReplaceableTest,swapMemberFunctionDelegatesToUserSwap)329 TEST(ReplaceableTest, swapMemberFunctionDelegatesToUserSwap) {
330   auto r1 = Replaceable<adl_test::UserDefinedSwap>{};
331   auto r2 = Replaceable<adl_test::UserDefinedSwap>{};
332   r1.swap(r2);
333   EXPECT_TRUE(r1->calledSwap);
334 }
335 
336 #if __cpp_deduction_guides >= 201703
TEST(ReplaceableTest,DeductionGuide)337 TEST(ReplaceableTest, DeductionGuide) {
338   Basic b{};
339   Replaceable r{b};
340   EXPECT_TRUE((std::is_same_v<Replaceable<Basic>, decltype(r)>));
341 }
342 #endif
343