1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <functional>
6#include <memory>
7
8#include <lib/fit/defer.h>
9#include <lib/fit/function.h>
10#include <unittest/unittest.h>
11
12namespace {
13
14// Counts instances.
15class balance {
16public:
17    balance(int* counter)
18        : counter_(counter) {
19        *counter_ += 1;
20    }
21
22    balance(balance&& other)
23        : counter_(other.counter_) {
24        *counter_ += 1;
25    }
26
27    ~balance() {
28        *counter_ -= 1;
29    }
30
31    balance(const balance& other) = delete;
32    balance& operator=(const balance& other) = delete;
33    balance& operator=(balance&& other) = delete;
34
35private:
36    int* const counter_;
37};
38
39void incr_arg(int* p) {
40    *p += 1;
41}
42
43template <typename T>
44bool default_construction() {
45    BEGIN_TEST;
46
47    fit::deferred_action<T> d;
48    EXPECT_FALSE(d);
49
50    END_TEST;
51}
52
53template <typename T>
54bool basic() {
55    BEGIN_TEST;
56
57    int var = 0;
58    {
59        auto do_incr = fit::defer<T>([&var]() { incr_arg(&var); });
60        EXPECT_TRUE(do_incr);
61        EXPECT_EQ(var, 0);
62    }
63    EXPECT_EQ(var, 1);
64
65    END_TEST;
66}
67
68template <typename T>
69bool cancel() {
70    BEGIN_TEST;
71
72    int var = 0;
73    {
74        auto do_incr = fit::defer<T>([&var]() { incr_arg(&var); });
75        EXPECT_TRUE(do_incr);
76        EXPECT_EQ(var, 0);
77
78        do_incr.cancel();
79        EXPECT_FALSE(do_incr);
80        EXPECT_EQ(var, 0);
81
82        // Once cancelled, call has no effect.
83        do_incr.call();
84        EXPECT_FALSE(do_incr);
85        EXPECT_EQ(var, 0);
86    }
87    EXPECT_EQ(var, 0);
88
89    END_TEST;
90}
91
92template <typename T>
93bool call() {
94    BEGIN_TEST;
95
96    int var = 0;
97    {
98        auto do_incr = fit::defer<T>([&var]() { incr_arg(&var); });
99        EXPECT_TRUE(do_incr);
100        EXPECT_EQ(var, 0);
101
102        do_incr.call();
103        EXPECT_FALSE(do_incr);
104        EXPECT_EQ(var, 1);
105
106        // Call is effective only once.
107        do_incr.call();
108        EXPECT_FALSE(do_incr);
109        EXPECT_EQ(var, 1);
110    }
111    EXPECT_EQ(var, 1);
112
113    END_TEST;
114}
115
116template <typename T>
117bool recursive_call() {
118    BEGIN_TEST;
119
120    int var = 0;
121    {
122        auto do_incr = fit::defer<T>([]() { /* no-op */ });
123        EXPECT_TRUE(do_incr);
124        do_incr = fit::defer<T>([&do_incr, &var]() {
125            incr_arg(&var);
126            do_incr.call();
127            EXPECT_FALSE(do_incr);
128        });
129        EXPECT_TRUE(do_incr);
130        EXPECT_EQ(var, 0);
131
132        do_incr.call();
133        EXPECT_FALSE(do_incr);
134        EXPECT_EQ(var, 1);
135    }
136    EXPECT_EQ(var, 1);
137
138    END_TEST;
139}
140
141template <typename T>
142bool move_construct_basic() {
143    BEGIN_TEST;
144
145    int var = 0;
146    {
147        auto do_incr = fit::defer<T>([&var]() { incr_arg(&var); });
148        EXPECT_TRUE(do_incr);
149
150        auto do_incr2(std::move(do_incr));
151        EXPECT_FALSE(do_incr);
152        EXPECT_TRUE(do_incr2);
153        EXPECT_EQ(var, 0);
154    }
155    EXPECT_EQ(var, 1);
156
157    END_TEST;
158}
159
160template <typename T>
161bool move_construct_from_canceled() {
162    BEGIN_TEST;
163
164    int var = 0;
165    {
166        auto do_incr = fit::defer<T>([&var]() { incr_arg(&var); });
167        EXPECT_TRUE(do_incr);
168
169        do_incr.cancel();
170        EXPECT_FALSE(do_incr);
171
172        auto do_incr2(std::move(do_incr));
173        EXPECT_FALSE(do_incr);
174        EXPECT_FALSE(do_incr2);
175        EXPECT_EQ(var, 0);
176    }
177    EXPECT_EQ(var, 0);
178
179    END_TEST;
180}
181
182template <typename T>
183bool move_construct_from_called() {
184    BEGIN_TEST;
185
186    int var = 0;
187    {
188        auto do_incr = fit::defer<T>([&var]() { incr_arg(&var); });
189        EXPECT_TRUE(do_incr);
190        EXPECT_EQ(var, 0);
191
192        do_incr.call();
193        EXPECT_FALSE(do_incr);
194        EXPECT_EQ(var, 1);
195
196        // Must not be called again, since do_incr has triggered already.
197        auto do_incr2(std::move(do_incr));
198        EXPECT_FALSE(do_incr);
199    }
200    EXPECT_EQ(var, 1);
201
202    END_TEST;
203}
204
205template <typename T>
206bool move_assign_basic() {
207    BEGIN_TEST;
208
209    int var1 = 0, var2 = 0;
210    {
211        auto do_incr = fit::defer<T>([&var1]() { incr_arg(&var1); });
212        auto do_incr2 = fit::defer<T>([&var2]() { incr_arg(&var2); });
213        EXPECT_TRUE(do_incr);
214        EXPECT_TRUE(do_incr2);
215        EXPECT_EQ(var1, 0);
216        EXPECT_EQ(var2, 0);
217
218        // do_incr2 is moved-to, so its associated function is called.
219        do_incr2 = std::move(do_incr);
220        EXPECT_FALSE(do_incr);
221        EXPECT_TRUE(do_incr2);
222        EXPECT_EQ(var1, 0);
223        EXPECT_EQ(var2, 1);
224    }
225    EXPECT_EQ(var1, 1);
226    EXPECT_EQ(var2, 1);
227
228    END_TEST;
229}
230
231template <typename T>
232bool move_assign_wider_scoped() {
233    BEGIN_TEST;
234
235    int var1 = 0, var2 = 0;
236    {
237        auto do_incr = fit::defer<T>([&var1]() { incr_arg(&var1); });
238        EXPECT_TRUE(do_incr);
239        EXPECT_EQ(var1, 0);
240        EXPECT_EQ(var2, 0);
241        {
242            auto do_incr2 = fit::defer<T>([&var2]() { incr_arg(&var2); });
243            EXPECT_TRUE(do_incr);
244            EXPECT_TRUE(do_incr2);
245            EXPECT_EQ(var1, 0);
246            EXPECT_EQ(var2, 0);
247
248            // do_incr is moved-to, so its associated function is called.
249            do_incr = std::move(do_incr2);
250            EXPECT_TRUE(do_incr);
251            EXPECT_FALSE(do_incr2);
252            EXPECT_EQ(var1, 1);
253            EXPECT_EQ(var2, 0);
254        }
255        // do_incr2 is out of scope but has been moved so its function is not
256        // called.
257        EXPECT_TRUE(do_incr);
258        EXPECT_EQ(var1, 1);
259        EXPECT_EQ(var2, 0);
260    }
261    EXPECT_EQ(var1, 1);
262    EXPECT_EQ(var2, 1);
263
264    END_TEST;
265}
266
267template <typename T>
268bool move_assign_from_canceled() {
269    BEGIN_TEST;
270
271    int var1 = 0, var2 = 0;
272    {
273        auto do_incr = fit::defer<T>([&var1]() { incr_arg(&var1); });
274        auto do_incr2 = fit::defer<T>([&var2]() { incr_arg(&var2); });
275        EXPECT_TRUE(do_incr);
276        EXPECT_TRUE(do_incr2);
277        EXPECT_EQ(var1, 0);
278        EXPECT_EQ(var2, 0);
279
280        do_incr.cancel();
281        EXPECT_FALSE(do_incr);
282        EXPECT_TRUE(do_incr2);
283        EXPECT_EQ(var1, 0);
284        EXPECT_EQ(var2, 0);
285
286        // do_incr2 is moved-to, so its associated function is called.
287        do_incr2 = std::move(do_incr);
288        EXPECT_FALSE(do_incr);
289        EXPECT_FALSE(do_incr2);
290        EXPECT_EQ(var1, 0);
291        EXPECT_EQ(var2, 1);
292    }
293    // do_incr was cancelled, this state is preserved by the move.
294    EXPECT_EQ(var1, 0);
295    EXPECT_EQ(var2, 1);
296
297    END_TEST;
298}
299
300template <typename T>
301bool move_assign_from_called() {
302    BEGIN_TEST;
303
304    int var1 = 0, var2 = 0;
305    {
306        auto do_incr = fit::defer<T>([&var1]() { incr_arg(&var1); });
307        auto do_incr2 = fit::defer<T>([&var2]() { incr_arg(&var2); });
308        EXPECT_TRUE(do_incr);
309        EXPECT_TRUE(do_incr2);
310        EXPECT_EQ(var1, 0);
311        EXPECT_EQ(var2, 0);
312
313        do_incr.call();
314        EXPECT_FALSE(do_incr);
315        EXPECT_TRUE(do_incr2);
316        EXPECT_EQ(var1, 1);
317        EXPECT_EQ(var2, 0);
318
319        // do_incr2 is moved-to, so its associated function is called.
320        do_incr2 = std::move(do_incr);
321        EXPECT_FALSE(do_incr);
322        EXPECT_FALSE(do_incr2);
323        EXPECT_EQ(var1, 1);
324        EXPECT_EQ(var2, 1);
325    }
326    // do_incr was called already, this state is preserved by the move.
327    EXPECT_EQ(var1, 1);
328    EXPECT_EQ(var2, 1);
329
330    END_TEST;
331}
332
333template <typename T>
334bool move_assign_to_null() {
335    BEGIN_TEST;
336
337    int call_count = 0;
338    {
339        fit::deferred_action<T> deferred(nullptr);
340        EXPECT_FALSE(deferred);
341        deferred = fit::defer<T>([&call_count] { call_count++; });
342        EXPECT_EQ(0, call_count);
343    }
344    EXPECT_EQ(1, call_count);
345
346    END_TEST;
347}
348
349template <typename T>
350bool move_assign_to_invalid() {
351    BEGIN_TEST;
352
353    int call_count = 0;
354    {
355        T fn;
356        fit::deferred_action<T> deferred(std::move(fn));
357        EXPECT_FALSE(deferred);
358        deferred = fit::defer<T>([&call_count] { call_count++; });
359        EXPECT_EQ(0, call_count);
360    }
361    EXPECT_EQ(1, call_count);
362
363    END_TEST;
364}
365
366template <typename T>
367bool target_destroyed_when_scope_exited() {
368    BEGIN_TEST;
369
370    int call_count = 0;
371    int instance_count = 0;
372    {
373        auto action = fit::defer<T>(
374            [&call_count, balance = balance(&instance_count)] {
375                incr_arg(&call_count);
376            });
377        EXPECT_EQ(0, call_count);
378        EXPECT_EQ(1, instance_count);
379    }
380    EXPECT_EQ(1, call_count);
381    EXPECT_EQ(0, instance_count);
382
383    END_TEST;
384}
385
386template <typename T>
387bool target_destroyed_when_called() {
388    BEGIN_TEST;
389
390    int call_count = 0;
391    int instance_count = 0;
392    {
393        auto action = fit::defer<T>(
394            [&call_count, balance = balance(&instance_count)] {
395                incr_arg(&call_count);
396            });
397        EXPECT_EQ(0, call_count);
398        EXPECT_EQ(1, instance_count);
399
400        action.call();
401        EXPECT_EQ(1, call_count);
402        EXPECT_EQ(0, instance_count);
403    }
404    EXPECT_EQ(1, call_count);
405    EXPECT_EQ(0, instance_count);
406
407    END_TEST;
408}
409
410template <typename T>
411bool target_destroyed_when_canceled() {
412    BEGIN_TEST;
413
414    int call_count = 0;
415    int instance_count = 0;
416    {
417        auto action = fit::defer<T>(
418            [&call_count, balance = balance(&instance_count)] {
419                incr_arg(&call_count);
420            });
421        EXPECT_EQ(0, call_count);
422        EXPECT_EQ(1, instance_count);
423
424        action.cancel();
425        EXPECT_EQ(0, call_count);
426        EXPECT_EQ(0, instance_count);
427    }
428    EXPECT_EQ(0, call_count);
429    EXPECT_EQ(0, instance_count);
430
431    END_TEST;
432}
433
434template <typename T>
435bool target_destroyed_when_move_constructed() {
436    BEGIN_TEST;
437
438    int call_count = 0;
439    int instance_count = 0;
440    {
441        auto action = fit::defer<T>(
442            [&call_count, balance = balance(&instance_count)] {
443                incr_arg(&call_count);
444            });
445        EXPECT_EQ(0, call_count);
446        EXPECT_EQ(1, instance_count);
447
448        auto action2(std::move(action));
449        EXPECT_EQ(0, call_count);
450        EXPECT_EQ(1, instance_count);
451    }
452    EXPECT_EQ(1, call_count);
453    EXPECT_EQ(0, instance_count);
454
455    END_TEST;
456}
457
458template <typename T>
459bool target_destroyed_when_move_assigned() {
460    BEGIN_TEST;
461
462    int call_count = 0;
463    int instance_count = 0;
464    {
465        auto action = fit::defer<T>(
466            [&call_count, balance = balance(&instance_count)] {
467                incr_arg(&call_count);
468            });
469        EXPECT_EQ(0, call_count);
470        EXPECT_EQ(1, instance_count);
471
472        auto action2 = fit::defer<T>([] {});
473        action2 = std::move(action);
474        EXPECT_EQ(0, call_count);
475        EXPECT_EQ(1, instance_count);
476    }
477    EXPECT_EQ(1, call_count);
478    EXPECT_EQ(0, instance_count);
479
480    END_TEST;
481}
482
483} // namespace
484
485BEGIN_TEST_CASE(defer_tests)
486RUN_TEST(default_construction<fit::closure>)
487RUN_TEST(default_construction<std::function<void()>>)
488RUN_TEST(basic<fit::closure>)
489RUN_TEST(basic<std::function<void()>>)
490RUN_TEST(cancel<fit::closure>)
491RUN_TEST(cancel<std::function<void()>>)
492RUN_TEST(call<fit::closure>)
493RUN_TEST(call<std::function<void()>>)
494RUN_TEST(recursive_call<fit::closure>)
495RUN_TEST(recursive_call<std::function<void()>>)
496RUN_TEST(move_construct_basic<fit::closure>)
497RUN_TEST(move_construct_basic<std::function<void()>>)
498RUN_TEST(move_construct_from_canceled<fit::closure>)
499RUN_TEST(move_construct_from_canceled<std::function<void()>>)
500RUN_TEST(move_construct_from_called<fit::closure>)
501RUN_TEST(move_construct_from_called<std::function<void()>>)
502RUN_TEST(move_assign_basic<fit::closure>)
503RUN_TEST(move_assign_basic<std::function<void()>>)
504RUN_TEST(move_assign_wider_scoped<fit::closure>)
505RUN_TEST(move_assign_wider_scoped<std::function<void()>>)
506RUN_TEST(move_assign_from_canceled<fit::closure>)
507RUN_TEST(move_assign_from_canceled<std::function<void()>>)
508RUN_TEST(move_assign_from_called<fit::closure>)
509RUN_TEST(move_assign_from_called<std::function<void()>>)
510RUN_TEST(move_assign_to_null<fit::closure>)
511RUN_TEST(move_assign_to_null<std::function<void()>>)
512RUN_TEST(move_assign_to_invalid<fit::closure>)
513RUN_TEST(move_assign_to_invalid<std::function<void()>>)
514// These tests do not support std::function because std::function copies
515// the captured values (which balance does not support).
516RUN_TEST(target_destroyed_when_scope_exited<fit::closure>)
517RUN_TEST(target_destroyed_when_called<fit::closure>)
518RUN_TEST(target_destroyed_when_canceled<fit::closure>)
519RUN_TEST(target_destroyed_when_move_constructed<fit::closure>)
520RUN_TEST(target_destroyed_when_move_assigned<fit::closure>)
521END_TEST_CASE(defer_tests)
522