1 | /* |
2 | * Copyright 2015-present Facebook, Inc. |
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/futures/Future.h> |
18 | #include <folly/portability/GTest.h> |
19 | |
20 | #include <thread> |
21 | |
22 | using namespace folly; |
23 | |
24 | namespace { |
25 | struct Widget { |
26 | int v_, copied_, moved_; |
27 | /* implicit */ Widget(int v) : v_(v), copied_(0), moved_(0) {} |
28 | Widget(const Widget& other) |
29 | : v_(other.v_), copied_(other.copied_ + 1), moved_(other.moved_) {} |
30 | Widget(Widget&& other) noexcept |
31 | : v_(other.v_), copied_(other.copied_), moved_(other.moved_ + 1) {} |
32 | Widget& operator=(const Widget& /* other */) { |
33 | throw std::logic_error("unexpected copy assignment" ); |
34 | } |
35 | Widget& operator=(Widget&& /* other */) { |
36 | throw std::logic_error("unexpected move assignment" ); |
37 | } |
38 | }; |
39 | |
40 | struct CountedWidget : Widget { |
41 | static std::vector<Widget*> instances_; |
42 | bool alive = true; |
43 | /* implicit */ CountedWidget(int v) : Widget(v) { |
44 | instances_.push_back(this); |
45 | } |
46 | CountedWidget(const CountedWidget& other) : Widget(other) { |
47 | instances_.push_back(this); |
48 | } |
49 | CountedWidget(CountedWidget&& other) noexcept(false) |
50 | : Widget(std::move(other)) { |
51 | other.alive = false; |
52 | other.remove(); |
53 | instances_.push_back(this); |
54 | } |
55 | ~CountedWidget() { |
56 | if (alive) { |
57 | remove(); |
58 | } |
59 | } |
60 | |
61 | private: |
62 | CountedWidget& operator=(const CountedWidget&) = delete; |
63 | CountedWidget& operator=(CountedWidget&&) = delete; |
64 | |
65 | void remove() { |
66 | auto iter = std::find(instances_.begin(), instances_.end(), this); |
67 | EXPECT_TRUE(iter != instances_.end()); |
68 | instances_.erase(iter); |
69 | } |
70 | }; |
71 | |
72 | std::vector<Widget*> CountedWidget::instances_; |
73 | } // namespace |
74 | |
75 | TEST(Then, tryConstructor) { |
76 | auto t = Try<Widget>(23); |
77 | EXPECT_EQ(t.value().v_, 23); |
78 | EXPECT_EQ(t.value().copied_, 0); |
79 | EXPECT_EQ(t.value().moved_, 1); |
80 | } |
81 | |
82 | TEST(Then, makeFuture) { |
83 | auto future = makeFuture<Widget>(23); |
84 | EXPECT_EQ(future.value().v_, 23); |
85 | EXPECT_EQ(future.value().copied_, 0); |
86 | EXPECT_EQ(future.value().moved_, 2); |
87 | } |
88 | |
89 | TEST(Then, tryConstRValueReference) { |
90 | auto future = makeFuture<Widget>(23).then([](const Try<Widget>&& t) { |
91 | EXPECT_EQ(t.value().copied_, 0); |
92 | EXPECT_EQ(t.value().moved_, 2); |
93 | return t.value().v_; |
94 | }); |
95 | EXPECT_EQ(future.value(), 23); |
96 | } |
97 | |
98 | TEST(Then, tryRValueReference) { |
99 | auto future = makeFuture<Widget>(23).then([](Try<Widget>&& t) { |
100 | EXPECT_EQ(t.value().copied_, 0); |
101 | EXPECT_EQ(t.value().moved_, 2); |
102 | return t.value().v_; |
103 | }); |
104 | EXPECT_EQ(future.value(), 23); |
105 | } |
106 | |
107 | TEST(Then, tryConstLValueReference) { |
108 | auto future = makeFuture<Widget>(23).then([](const Try<Widget>& t) { |
109 | EXPECT_EQ(t.value().copied_, 0); |
110 | EXPECT_EQ(t.value().moved_, 2); |
111 | return t.value().v_; |
112 | }); |
113 | EXPECT_EQ(future.value(), 23); |
114 | } |
115 | |
116 | TEST(Then, tryValue) { |
117 | auto future = makeFuture<Widget>(23).then([](Try<Widget> t) { |
118 | EXPECT_EQ(t.value().copied_, 0); |
119 | EXPECT_EQ(t.value().moved_, 3); |
120 | return t.value().v_; |
121 | }); |
122 | EXPECT_EQ(future.value(), 23); |
123 | } |
124 | |
125 | TEST(Then, tryConstValue) { |
126 | auto future = makeFuture<Widget>(23).then([](const Try<Widget> t) { |
127 | EXPECT_EQ(t.value().copied_, 0); |
128 | EXPECT_EQ(t.value().moved_, 3); |
129 | return t.value().v_; |
130 | }); |
131 | EXPECT_EQ(future.value(), 23); |
132 | } |
133 | |
134 | TEST(Then, constRValueReference) { |
135 | auto future = makeFuture<Widget>(23).thenValue([](const Widget&& w) { |
136 | EXPECT_EQ(w.copied_, 0); |
137 | EXPECT_EQ(w.moved_, 2); |
138 | return w.v_; |
139 | }); |
140 | EXPECT_EQ(future.value(), 23); |
141 | } |
142 | |
143 | TEST(Then, rValueReference) { |
144 | auto future = makeFuture<Widget>(23).thenValue([](Widget&& w) { |
145 | EXPECT_EQ(w.copied_, 0); |
146 | EXPECT_EQ(w.moved_, 2); |
147 | return w.v_; |
148 | }); |
149 | EXPECT_EQ(future.value(), 23); |
150 | } |
151 | |
152 | TEST(Then, constLValueReference) { |
153 | auto future = makeFuture<Widget>(23).thenValue([](const Widget& w) { |
154 | EXPECT_EQ(w.copied_, 0); |
155 | EXPECT_EQ(w.moved_, 2); |
156 | return w.v_; |
157 | }); |
158 | EXPECT_EQ(future.value(), 23); |
159 | } |
160 | |
161 | TEST(Then, value) { |
162 | auto future = makeFuture<Widget>(23).thenValue([](Widget w) { |
163 | EXPECT_EQ(w.copied_, 0); |
164 | EXPECT_EQ(w.moved_, 3); |
165 | return w.v_; |
166 | }); |
167 | EXPECT_EQ(future.value(), 23); |
168 | } |
169 | |
170 | TEST(Then, constValue) { |
171 | auto future = makeFuture<Widget>(23).thenValue([](const Widget w) { |
172 | EXPECT_EQ(w.copied_, 0); |
173 | EXPECT_EQ(w.moved_, 3); |
174 | return w.v_; |
175 | }); |
176 | EXPECT_EQ(future.value(), 23); |
177 | } |
178 | |
179 | TEST(Then, objectAliveDuringImmediateNoParamContinuation) { |
180 | auto f = makeFuture<CountedWidget>(23); |
181 | auto called = false; |
182 | std::move(f).thenValue([&](auto&&) { |
183 | EXPECT_EQ(CountedWidget::instances_.size(), 1u); |
184 | EXPECT_EQ(CountedWidget::instances_[0]->v_, 23); |
185 | called = true; |
186 | }); |
187 | EXPECT_EQ(true, called); |
188 | } |
189 | |
190 | TEST(Then, objectAliveDuringDeferredNoParamContinuation) { |
191 | auto p = Promise<CountedWidget>{}; |
192 | bool called = false; |
193 | p.getFuture().thenValue([&](auto&&) { |
194 | EXPECT_EQ(CountedWidget::instances_.size(), 1u); |
195 | EXPECT_EQ(CountedWidget::instances_[0]->v_, 23); |
196 | called = true; |
197 | }); |
198 | p.setValue(CountedWidget{23}); |
199 | EXPECT_EQ(true, called); |
200 | } |
201 | |
202 | TEST(Then, voidThenShouldPropagateExceptions) { |
203 | EXPECT_FALSE(makeFuture(42).then().hasException()); |
204 | EXPECT_TRUE(makeFuture<int>(std::runtime_error("err" )).then().hasException()); |
205 | } |
206 | |