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 <map>
6
7 #include <lib/fit/promise.h>
8 #include <unittest/unittest.h>
9
10 #include "unittest_utils.h"
11
12 namespace {
13
14 enum class disposition {
15 pending,
16 resumed,
17 released
18 };
19
20 class fake_resolver : public fit::suspended_task::resolver {
21 public:
num_tickets_issued() const22 uint64_t num_tickets_issued() const { return next_ticket_ - 1; }
23
obtain_ticket()24 fit::suspended_task::ticket obtain_ticket() {
25 fit::suspended_task::ticket ticket = next_ticket_++;
26 tickets_.emplace(ticket, disposition::pending);
27 return ticket;
28 }
29
get_disposition(fit::suspended_task::ticket ticket)30 disposition get_disposition(fit::suspended_task::ticket ticket) {
31 auto it = tickets_.find(ticket);
32 ASSERT_CRITICAL(it != tickets_.end());
33 return it->second;
34 }
35
duplicate_ticket(fit::suspended_task::ticket ticket)36 fit::suspended_task::ticket duplicate_ticket(
37 fit::suspended_task::ticket ticket) override {
38 auto it = tickets_.find(ticket);
39 ASSERT_CRITICAL(it != tickets_.end());
40 ASSERT_CRITICAL(it->second == disposition::pending);
41 return obtain_ticket();
42 }
43
resolve_ticket(fit::suspended_task::ticket ticket,bool resume_task)44 void resolve_ticket(
45 fit::suspended_task::ticket ticket, bool resume_task) override {
46 auto it = tickets_.find(ticket);
47 ASSERT_CRITICAL(it != tickets_.end());
48 ASSERT_CRITICAL(it->second == disposition::pending);
49 it->second = resume_task ? disposition::resumed : disposition::released;
50 }
51
52 private:
53 fit::suspended_task::ticket next_ticket_ = 1;
54 std::map<fit::suspended_task::ticket, disposition> tickets_;
55 };
56
test()57 bool test() {
58 BEGIN_TEST;
59
60 fake_resolver resolver;
61 {
62 fit::suspended_task empty1;
63 EXPECT_FALSE(empty1);
64
65 fit::suspended_task empty2(nullptr, 42);
66 EXPECT_FALSE(empty2);
67
68 fit::suspended_task empty_copy(empty1);
69 EXPECT_FALSE(empty_copy);
70 EXPECT_FALSE(empty1);
71
72 fit::suspended_task empty_move(std::move(empty2));
73 EXPECT_FALSE(empty_move);
74 EXPECT_FALSE(empty2);
75
76 fit::suspended_task task(&resolver, resolver.obtain_ticket());
77 EXPECT_TRUE(task);
78 EXPECT_EQ(1, resolver.num_tickets_issued());
79 EXPECT_EQ(disposition::pending, resolver.get_disposition(1));
80
81 fit::suspended_task task_copy(task);
82 EXPECT_TRUE(task_copy);
83 EXPECT_TRUE(task);
84 EXPECT_EQ(2, resolver.num_tickets_issued());
85 EXPECT_EQ(disposition::pending, resolver.get_disposition(1));
86 EXPECT_EQ(disposition::pending, resolver.get_disposition(2));
87
88 fit::suspended_task task_move(std::move(task));
89 EXPECT_TRUE(task_move);
90 EXPECT_FALSE(task);
91 EXPECT_EQ(2, resolver.num_tickets_issued());
92 EXPECT_EQ(disposition::pending, resolver.get_disposition(1));
93 EXPECT_EQ(disposition::pending, resolver.get_disposition(2));
94
95 fit::suspended_task x;
96 x = empty1;
97 EXPECT_FALSE(x);
98
99 x = task_copy;
100 EXPECT_TRUE(x);
101 EXPECT_TRUE(task_copy);
102 EXPECT_EQ(3, resolver.num_tickets_issued());
103 EXPECT_EQ(disposition::pending, resolver.get_disposition(1));
104 EXPECT_EQ(disposition::pending, resolver.get_disposition(2));
105 EXPECT_EQ(disposition::pending, resolver.get_disposition(3));
106
107 x = std::move(empty_move); // x's ticket is released here
108 EXPECT_FALSE(x);
109 EXPECT_FALSE(empty_move);
110 EXPECT_EQ(3, resolver.num_tickets_issued());
111 EXPECT_EQ(disposition::pending, resolver.get_disposition(1));
112 EXPECT_EQ(disposition::pending, resolver.get_disposition(2));
113 EXPECT_EQ(disposition::released, resolver.get_disposition(3));
114
115 x = task_copy; // assign x a duplicate ticket
116 x = std::move(task_move); // x's ticket is released here
117 EXPECT_TRUE(x);
118 EXPECT_TRUE(task_copy);
119 EXPECT_FALSE(task_move);
120 EXPECT_EQ(4, resolver.num_tickets_issued());
121 EXPECT_EQ(disposition::pending, resolver.get_disposition(1));
122 EXPECT_EQ(disposition::pending, resolver.get_disposition(2));
123 EXPECT_EQ(disposition::released, resolver.get_disposition(3));
124 EXPECT_EQ(disposition::released, resolver.get_disposition(4));
125
126 x.resume_task(); // x's ticket is resumed here
127 EXPECT_FALSE(x);
128 EXPECT_EQ(4, resolver.num_tickets_issued());
129 EXPECT_EQ(disposition::resumed, resolver.get_disposition(1));
130 EXPECT_EQ(disposition::pending, resolver.get_disposition(2));
131 EXPECT_EQ(disposition::released, resolver.get_disposition(3));
132 EXPECT_EQ(disposition::released, resolver.get_disposition(4));
133
134 x.resume_task(); // already resumed so has no effect
135 EXPECT_FALSE(x);
136
137 x.reset(); // already resumed so has no effect
138 EXPECT_FALSE(x);
139
140 // note: task_copy still has a ticket here which will be
141 // released when the scope exits
142 }
143 EXPECT_EQ(4, resolver.num_tickets_issued());
144 EXPECT_EQ(disposition::resumed, resolver.get_disposition(1));
145 EXPECT_EQ(disposition::released, resolver.get_disposition(2));
146 EXPECT_EQ(disposition::released, resolver.get_disposition(3));
147 EXPECT_EQ(disposition::released, resolver.get_disposition(4));
148
149 END_TEST;
150 }
151
swapping()152 bool swapping() {
153 BEGIN_TEST;
154
155 fake_resolver resolver;
156 {
157 fit::suspended_task a(&resolver, resolver.obtain_ticket());
158 fit::suspended_task b(&resolver, resolver.obtain_ticket());
159 fit::suspended_task c;
160 EXPECT_EQ(2, resolver.num_tickets_issued());
161 EXPECT_EQ(disposition::pending, resolver.get_disposition(1));
162 EXPECT_EQ(disposition::pending, resolver.get_disposition(2));
163
164 a.swap(c);
165 EXPECT_FALSE(a);
166 EXPECT_TRUE(c);
167 EXPECT_EQ(disposition::pending, resolver.get_disposition(1));
168 EXPECT_EQ(disposition::pending, resolver.get_disposition(2));
169
170 a.swap(a);
171 EXPECT_FALSE(a);
172 EXPECT_EQ(disposition::pending, resolver.get_disposition(1));
173 EXPECT_EQ(disposition::pending, resolver.get_disposition(2));
174
175 swap(c, b);
176 EXPECT_TRUE(c);
177 EXPECT_TRUE(b);
178 EXPECT_EQ(disposition::pending, resolver.get_disposition(1));
179 EXPECT_EQ(disposition::pending, resolver.get_disposition(2));
180
181 swap(c, c);
182 EXPECT_TRUE(c);
183 EXPECT_EQ(disposition::pending, resolver.get_disposition(1));
184 EXPECT_EQ(disposition::pending, resolver.get_disposition(2));
185
186 c.resume_task();
187 EXPECT_FALSE(c);
188 EXPECT_EQ(disposition::pending, resolver.get_disposition(1));
189 EXPECT_EQ(disposition::resumed, resolver.get_disposition(2));
190
191 b.reset();
192 EXPECT_FALSE(b);
193 EXPECT_EQ(disposition::released, resolver.get_disposition(1));
194 EXPECT_EQ(disposition::resumed, resolver.get_disposition(2));
195 }
196
197 END_TEST;
198 }
199
200 } // namespace
201
202 BEGIN_TEST_CASE(suspended_task_tests)
203 RUN_TEST(test)
204 RUN_TEST(swapping)
205 END_TEST_CASE(suspended_task_tests)
206