blob: fa954196196b0ad899b5339a537049ea04f0634f [file] [log] [blame]
William A. Kennington IIIe847ef82018-11-02 17:29:15 -07001#include <stdplus/handle/copyable.hpp>
Patrick Williamsd1984dd2023-05-10 16:12:44 -05002
3#include <optional>
William A. Kennington IIIe847ef82018-11-02 17:29:15 -07004#include <string>
5#include <utility>
6#include <vector>
7
Patrick Williamsd1984dd2023-05-10 16:12:44 -05008#include <gtest/gtest.h>
9
William A. Kennington IIIe847ef82018-11-02 17:29:15 -070010namespace stdplus
11{
12namespace
13{
14
15static std::vector<int> reffed;
16static int stored_ref = 0;
17static std::vector<int> dropped;
18static int stored_drop = 0;
19
William A. Kennington III7910ae02019-11-11 12:13:25 -080020struct SimpleRef
21{
22 int operator()(const int& i) noexcept
23 {
24 reffed.push_back(i);
25 return i + 1;
26 }
27};
28
William A. Kennington IIIe847ef82018-11-02 17:29:15 -070029int ref(const int& i)
30{
William A. Kennington III7910ae02019-11-11 12:13:25 -080031 return SimpleRef()(i);
William A. Kennington IIIe847ef82018-11-02 17:29:15 -070032}
33
William A. Kennington III7910ae02019-11-11 12:13:25 -080034struct SimpleDrop
35{
36 void operator()(int&& i) noexcept
37 {
38 dropped.push_back(std::move(i));
39 }
40};
41
William A. Kennington IIIe847ef82018-11-02 17:29:15 -070042void drop(int&& i)
43{
William A. Kennington III7910ae02019-11-11 12:13:25 -080044 SimpleDrop()(std::move(i));
William A. Kennington IIIe847ef82018-11-02 17:29:15 -070045}
46
William A. Kennington III7910ae02019-11-11 12:13:25 -080047struct StoreRef
William A. Kennington IIIe847ef82018-11-02 17:29:15 -070048{
William A. Kennington III7910ae02019-11-11 12:13:25 -080049 int operator()(const int& i, std::string&, int& si)
50 {
51 reffed.push_back(i);
52 // Make sure we can update the stored data
53 stored_ref = si++;
54 return i + 1;
55 }
56};
William A. Kennington IIIe847ef82018-11-02 17:29:15 -070057
William A. Kennington III7910ae02019-11-11 12:13:25 -080058struct StoreDrop
William A. Kennington IIIe847ef82018-11-02 17:29:15 -070059{
William A. Kennington III7910ae02019-11-11 12:13:25 -080060 void operator()(int&& i, std::string&, int& si)
61 {
62 dropped.push_back(std::move(i));
63 // Make sure we can update the stored data
64 stored_drop = si++;
65 }
66};
William A. Kennington IIIe847ef82018-11-02 17:29:15 -070067
William A. Kennington III7910ae02019-11-11 12:13:25 -080068using SimpleHandleOld = Copyable<int>::Handle<drop, ref>;
69using SimpleHandle = Copyable<int>::HandleF<SimpleDrop, SimpleRef>;
70using StoreHandle =
71 Copyable<int, std::string, int>::HandleF<StoreDrop, StoreRef>;
William A. Kennington IIIe847ef82018-11-02 17:29:15 -070072
William A. Kennington III1007be92019-11-11 12:18:37 -080073static_assert(std::is_nothrow_copy_constructible_v<SimpleHandle>);
74static_assert(std::is_nothrow_move_constructible_v<SimpleHandle>);
75static_assert(std::is_nothrow_move_assignable_v<SimpleHandle>);
76static_assert(std::is_nothrow_destructible_v<SimpleHandle>);
77static_assert(noexcept(std::declval<SimpleHandle>().reset()));
78static_assert(!std::is_nothrow_copy_constructible_v<StoreHandle>);
79// http://cplusplus.github.io/LWG/lwg-active.html#2116
80// static_assert(std::is_nothrow_move_constructible_v<StoreHandle>);
81static_assert(!std::is_nothrow_move_assignable_v<StoreHandle>);
82static_assert(!std::is_nothrow_destructible_v<StoreHandle>);
83static_assert(!noexcept(std::declval<StoreHandle>().reset()));
84
William A. Kennington IIIe847ef82018-11-02 17:29:15 -070085class CopyableHandleTest : public ::testing::Test
86{
87 protected:
88 void SetUp()
89 {
90 reffed.clear();
91 dropped.clear();
92 }
93
94 void TearDown()
95 {
96 EXPECT_TRUE(reffed.empty());
97 EXPECT_TRUE(dropped.empty());
98 }
99};
100
101TEST_F(CopyableHandleTest, EmptyNoStorage)
102{
William A. Kennington III7910ae02019-11-11 12:13:25 -0800103 SimpleHandleOld h(std::nullopt);
William A. Kennington IIIe847ef82018-11-02 17:29:15 -0700104 EXPECT_FALSE(h);
105 EXPECT_THROW(h.value(), std::bad_optional_access);
106 h.reset();
107 EXPECT_FALSE(h);
108 EXPECT_THROW(h.value(), std::bad_optional_access);
109}
110
111TEST_F(CopyableHandleTest, EmptyWithStorage)
112{
113 auto maybeV = std::nullopt;
114 StoreHandle h(maybeV, "str", 5);
115 EXPECT_FALSE(h);
116 EXPECT_THROW(h.value(), std::bad_optional_access);
117 h.reset(maybeV);
118 EXPECT_FALSE(h);
119 EXPECT_THROW(h.value(), std::bad_optional_access);
120}
121
122TEST_F(CopyableHandleTest, SimplePopulated)
123{
124 constexpr int expected = 3;
125 {
126 int val = expected;
127 SimpleHandle h(std::move(val));
128 EXPECT_TRUE(h);
129 EXPECT_EQ(expected, *h);
130 EXPECT_EQ(expected, h.value());
131 EXPECT_TRUE(dropped.empty());
132 }
133 EXPECT_EQ(std::vector{expected}, dropped);
134 dropped.clear();
135}
136
137TEST_F(CopyableHandleTest, OptionalPopulated)
138{
139 constexpr int expected = 3;
140 {
141 std::optional<int> maybeVal{expected};
142 SimpleHandle h(std::move(maybeVal));
143 EXPECT_TRUE(h);
144 EXPECT_EQ(expected, *h);
145 EXPECT_EQ(expected, h.value());
146 EXPECT_TRUE(dropped.empty());
147 }
148 EXPECT_TRUE(reffed.empty());
149 EXPECT_EQ(std::vector{expected}, dropped);
150 dropped.clear();
151 {
152 const std::optional<int> maybeVal{expected};
153 SimpleHandle h(maybeVal);
154 EXPECT_TRUE(h);
155 EXPECT_EQ(expected + 1, *h);
156 EXPECT_EQ(expected + 1, h.value());
157 EXPECT_EQ(std::vector{expected}, reffed);
158 reffed.clear();
159 EXPECT_TRUE(dropped.empty());
160 }
161 EXPECT_EQ(std::vector{expected + 1}, dropped);
162 dropped.clear();
163}
164
165TEST_F(CopyableHandleTest, SimplePopulatedWithStorage)
166{
167 constexpr int expected = 3;
168 {
169 StoreHandle h(expected, std::string{"str"}, 5);
170 EXPECT_TRUE(h);
171 EXPECT_EQ(expected + 1, *h);
172 EXPECT_EQ(expected + 1, h.value());
173 EXPECT_EQ(5, stored_ref);
174 EXPECT_EQ(std::vector{expected}, reffed);
175 reffed.clear();
176 EXPECT_TRUE(dropped.empty());
177 }
178 EXPECT_EQ(6, stored_drop);
179 EXPECT_EQ(std::vector{expected + 1}, dropped);
180 dropped.clear();
181}
182
183TEST_F(CopyableHandleTest, ResetPopulatedWithStorage)
184{
185 constexpr int expected = 3;
186 const std::string s{"str"};
187 StoreHandle h(int{expected}, s, 5);
188 EXPECT_TRUE(dropped.empty());
189 h.reset(std::nullopt);
190 EXPECT_FALSE(h);
191 EXPECT_THROW(h.value(), std::bad_optional_access);
192 EXPECT_EQ(5, stored_drop);
193 EXPECT_EQ(std::vector{expected}, dropped);
194 dropped.clear();
195}
196
197TEST_F(CopyableHandleTest, ResetNewPopulated)
198{
199 constexpr int expected = 3, expected2 = 10;
200 {
201 SimpleHandle h(int{expected});
202 EXPECT_TRUE(dropped.empty());
203 h.reset(int{expected2});
204 EXPECT_TRUE(h);
205 EXPECT_EQ(expected2, *h);
206 EXPECT_EQ(expected2, h.value());
207 EXPECT_EQ(std::vector{expected}, dropped);
208 dropped.clear();
209 }
210 EXPECT_EQ(std::vector{expected2}, dropped);
211 dropped.clear();
212}
213
214TEST_F(CopyableHandleTest, ResetCopyPopulated)
215{
216 constexpr int expected = 3, expected2 = 10;
217 {
218 SimpleHandle h(int{expected});
219 EXPECT_TRUE(reffed.empty());
220 EXPECT_TRUE(dropped.empty());
221 const std::optional<int> maybe2{expected2};
222 h.reset(maybe2);
223 EXPECT_TRUE(h);
224 EXPECT_EQ(expected2 + 1, *h);
225 EXPECT_EQ(expected2 + 1, h.value());
226 EXPECT_EQ(std::vector{expected2}, reffed);
227 reffed.clear();
228 EXPECT_EQ(std::vector{expected}, dropped);
229 dropped.clear();
230 }
231 EXPECT_EQ(std::vector{expected2 + 1}, dropped);
232 dropped.clear();
233}
234
235TEST_F(CopyableHandleTest, ResetCopyPopulatedWithStorage)
236{
237 constexpr int expected = 3, expected2 = 10;
238 {
239 StoreHandle h(int{expected}, "str", 5);
240 EXPECT_TRUE(dropped.empty());
241 h.reset(expected2);
242 EXPECT_TRUE(h);
243 EXPECT_EQ(expected2 + 1, *h);
244 EXPECT_EQ(expected2 + 1, h.value());
245 EXPECT_EQ(5, stored_ref);
246 EXPECT_EQ(std::vector{expected2}, reffed);
247 reffed.clear();
248 EXPECT_EQ(6, stored_drop);
249 EXPECT_EQ(std::vector{expected}, dropped);
250 dropped.clear();
251 }
252 EXPECT_EQ(7, stored_drop);
253 EXPECT_EQ(std::vector{expected2 + 1}, dropped);
254 dropped.clear();
255}
256
257TEST_F(CopyableHandleTest, MoveConstructWithStorage)
258{
259 constexpr int expected = 3;
260 StoreHandle h1(int{expected}, "str", 5);
261 {
262 StoreHandle h2(std::move(h1));
263 EXPECT_TRUE(dropped.empty());
264 EXPECT_FALSE(h1);
William A. Kennington III49d92692023-06-06 14:02:01 -0700265 // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move)
William A. Kennington IIIe847ef82018-11-02 17:29:15 -0700266 EXPECT_THROW(h1.value(), std::bad_optional_access);
267 EXPECT_TRUE(h2);
268 EXPECT_EQ(expected, *h2);
269 EXPECT_EQ(expected, h2.value());
270 }
271 EXPECT_EQ(5, stored_drop);
272 EXPECT_EQ(std::vector{expected}, dropped);
273 dropped.clear();
274}
275
276TEST_F(CopyableHandleTest, MoveAssignWithStorage)
277{
278 constexpr int expected = 3, expected2 = 10;
279 {
280 StoreHandle h1(int{expected}, "str", 5);
281 StoreHandle h2(int{expected2}, "str", 10);
282 EXPECT_TRUE(dropped.empty());
283
284 h2 = std::move(h1);
285 EXPECT_EQ(10, stored_drop);
286 EXPECT_EQ(std::vector{expected2}, dropped);
287 dropped.clear();
288 EXPECT_FALSE(h1);
William A. Kennington III49d92692023-06-06 14:02:01 -0700289 // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move)
William A. Kennington IIIe847ef82018-11-02 17:29:15 -0700290 EXPECT_THROW(h1.value(), std::bad_optional_access);
291 EXPECT_TRUE(h2);
292 EXPECT_EQ(expected, *h2);
293 EXPECT_EQ(expected, h2.value());
294 }
295 EXPECT_EQ(5, stored_drop);
296 EXPECT_EQ(std::vector{expected}, dropped);
297 dropped.clear();
298}
299
300TEST_F(CopyableHandleTest, CopyConstructSrcEmptyWithStorage)
301{
302 StoreHandle h1(std::nullopt, "str", 5);
303 StoreHandle h2(h1);
304}
305
306TEST_F(CopyableHandleTest, CopyConstructWithStorage)
307{
308 constexpr int expected = 3;
309 StoreHandle h1(int{expected}, "str", 5);
310 StoreHandle h2(h1);
311 EXPECT_EQ(5, stored_ref);
312 EXPECT_EQ(std::vector{expected}, reffed);
313 reffed.clear();
314
315 h1.reset();
316 EXPECT_EQ(5, stored_drop);
317 EXPECT_EQ(std::vector{expected}, dropped);
318 dropped.clear();
319 h2.reset();
320 EXPECT_EQ(6, stored_drop);
321 EXPECT_EQ(std::vector{expected + 1}, dropped);
322 dropped.clear();
323}
324
325TEST_F(CopyableHandleTest, CopyAssignBothEmptyWithStorage)
326{
327 StoreHandle h1(std::nullopt, "str", 5);
328 StoreHandle h2(std::nullopt, "str", 10);
329 h2 = h1;
330}
331
332TEST_F(CopyableHandleTest, CopyAssignSrcEmptyWithStorage)
333{
334 constexpr int expected = 3;
335 StoreHandle h1(std::nullopt, "str", 5);
336 StoreHandle h2(int{expected}, "str", 10);
337 h2 = h1;
338 EXPECT_EQ(10, stored_drop);
339 EXPECT_EQ(std::vector{expected}, dropped);
340 dropped.clear();
341}
342
343TEST_F(CopyableHandleTest, CopyAssignDstEmptyWithStorage)
344{
345 constexpr int expected = 3;
346 StoreHandle h1(int{expected}, "str", 5);
347 StoreHandle h2(std::nullopt, "str", 10);
348 h2 = h1;
349 EXPECT_EQ(5, stored_ref);
350 EXPECT_EQ(std::vector{expected}, reffed);
351 reffed.clear();
352
353 h1.reset();
354 EXPECT_EQ(5, stored_drop);
355 EXPECT_EQ(std::vector{expected}, dropped);
356 dropped.clear();
357 h2.reset();
358 EXPECT_EQ(6, stored_drop);
359 EXPECT_EQ(std::vector{expected + 1}, dropped);
360 dropped.clear();
361}
362
363TEST_F(CopyableHandleTest, CopyAssignWithStorage)
364{
365 constexpr int expected = 3, expected2 = 15;
366 StoreHandle h1(int{expected}, "str", 5);
367 StoreHandle h2(int{expected2}, "str", 10);
368 h2 = h1;
369 EXPECT_EQ(10, stored_drop);
370 EXPECT_EQ(std::vector{expected2}, dropped);
371 dropped.clear();
372 EXPECT_EQ(5, stored_ref);
373 EXPECT_EQ(std::vector{expected}, reffed);
374 reffed.clear();
375
376 h1.reset();
377 EXPECT_EQ(5, stored_drop);
378 EXPECT_EQ(std::vector{expected}, dropped);
379 dropped.clear();
380 h2.reset();
381 EXPECT_EQ(6, stored_drop);
382 EXPECT_EQ(std::vector{expected + 1}, dropped);
383 dropped.clear();
384}
385
386} // namespace
387} // namespace stdplus