proxygen
InlineTaskTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017-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/Portability.h>
18 
19 #if FOLLY_HAS_COROUTINES
20 
25 
26 #include <tuple>
27 
28 template <typename T>
29 using InlineTask = folly::coro::detail::InlineTask<T>;
30 
31 TEST(InlineTask, CallVoidTaskWithoutAwaitingNeverRuns) {
32  bool hasStarted = false;
33  auto f = [&]() -> InlineTask<void> {
34  hasStarted = true;
35  co_return;
36  };
37  {
38  auto task = f();
39  EXPECT_FALSE(hasStarted);
40  }
41  EXPECT_FALSE(hasStarted);
42 }
43 
44 TEST(InlineTask, CallValueTaskWithoutAwaitingNeverRuns) {
45  bool hasStarted = false;
46  auto f = [&]() -> InlineTask<int> {
47  hasStarted = true;
48  co_return 123;
49  };
50  {
51  auto task = f();
52  EXPECT_FALSE(hasStarted);
53  }
54  EXPECT_FALSE(hasStarted);
55 }
56 
57 TEST(InlineTask, CallRefTaskWithoutAwaitingNeverRuns) {
58  bool hasStarted = false;
59  int value;
60  auto f = [&]() -> InlineTask<int&> {
61  hasStarted = true;
62  co_return value;
63  };
64  {
65  auto task = f();
66  EXPECT_FALSE(hasStarted);
67  }
68  EXPECT_FALSE(hasStarted);
69 }
70 
71 TEST(InlineTask, SimpleVoidTask) {
72  bool hasRun = false;
73  auto f = [&]() -> InlineTask<void> {
74  hasRun = true;
75  co_return;
76  };
77  auto t = f();
78  EXPECT_FALSE(hasRun);
80  EXPECT_TRUE(hasRun);
81 }
82 
83 TEST(InlineTask, SimpleValueTask) {
84  bool hasRun = false;
85  auto f = [&]() -> InlineTask<int> {
86  hasRun = true;
87  co_return 42;
88  };
89  auto t = f();
90  EXPECT_FALSE(hasRun);
92  EXPECT_TRUE(hasRun);
93 }
94 
95 TEST(InlineTask, SimpleRefTask) {
96  bool hasRun = false;
97  auto f = [&]() -> InlineTask<bool&> {
98  hasRun = true;
99  co_return hasRun;
100  };
101 
102  auto t = f();
103  EXPECT_FALSE(hasRun);
104  auto& result = folly::coro::blockingWait(std::move(t));
105  EXPECT_TRUE(hasRun);
106  EXPECT_EQ(&hasRun, &result);
107 }
108 
109 struct MoveOnlyType {
110  int value_;
111 
112  explicit MoveOnlyType(int value) noexcept : value_(value) {}
113 
114  MoveOnlyType(MoveOnlyType&& other) noexcept
115  : value_(std::exchange(other.value_, -1)) {}
116 
117  MoveOnlyType& operator=(MoveOnlyType&& other) noexcept {
118  value_ = std::exchange(other.value_, -1);
119  return *this;
120  }
121 
122  ~MoveOnlyType() {
123  value_ = -2;
124  }
125 };
126 
127 struct TypeWithImplicitSingleValueConstructor {
128  float value_;
129  /* implicit */ TypeWithImplicitSingleValueConstructor(float x) : value_(x) {}
130 };
131 
132 TEST(InlineTask, ReturnValueWithInitializerListSyntax) {
133  auto f = []() -> InlineTask<TypeWithImplicitSingleValueConstructor> {
134  co_return{1.23f};
135  };
136 
137  auto result = folly::coro::blockingWait(f());
138  EXPECT_EQ(1.23f, result.value_);
139 }
140 
141 struct TypeWithImplicitMultiValueConstructor {
142  std::string s_;
143  float x_;
144  /* implicit */ TypeWithImplicitMultiValueConstructor(
145  std::string s,
146  float x) noexcept
147  : s_(s), x_(x) {}
148 };
149 
150 TEST(InlineTask, ReturnValueWithInitializerListSyntax2) {
151  auto f = []() -> InlineTask<TypeWithImplicitMultiValueConstructor> {
152 #if 0
153  // Under clang:
154  // error: cannot compile this scalar expression yet.
155  co_return{"hello", 3.1415f};
156 #else
157  co_return TypeWithImplicitMultiValueConstructor{"hello", 3.1415f};
158 #endif
159  };
160 
161  auto result = folly::coro::blockingWait(f());
162  EXPECT_EQ("hello", result.s_);
163  EXPECT_EQ(3.1415f, result.x_);
164 }
165 
166 TEST(InlineTask, TaskOfMoveOnlyType) {
167  auto f = []() -> InlineTask<MoveOnlyType> { co_return MoveOnlyType{42}; };
168 
169  auto x = folly::coro::blockingWait(f());
170  EXPECT_EQ(42, x.value_);
171 
172  bool executed = false;
173  auto g = [&]() -> InlineTask<void> {
174  auto result = co_await f();
175  EXPECT_EQ(42, result.value_);
176  executed = true;
177  };
178 
180 
181  EXPECT_TRUE(executed);
182 }
183 
184 TEST(InlineTask, MoveOnlyTypeNRVO) {
185  auto f = []() -> InlineTask<MoveOnlyType> {
186  MoveOnlyType x{10};
187 
188  // Shouldn't need std::move(x) here, according to
189  // N4760 15.8.3(3) Copy/move elision
190  co_return std::move(x);
191  };
192 
193  auto x = folly::coro::blockingWait(f());
194  EXPECT_EQ(10, x.value_);
195 }
196 
197 TEST(InlineTask, ReturnLvalueReference) {
198  int value = 0;
199  auto f = [&]() -> InlineTask<int&> { co_return value; };
200 
201  auto& x = folly::coro::blockingWait(f());
202  EXPECT_EQ(&value, &x);
203 }
204 
205 struct MyException : std::exception {};
206 
207 TEST(InlineTask, ExceptionsPropagateFromVoidTask) {
208  auto f = []() -> InlineTask<void> {
209  co_await folly::coro::Baton{true};
210  throw MyException{};
211  };
213 }
214 
215 TEST(InlineTask, ExceptionsPropagateFromValueTask) {
216  auto f = []() -> InlineTask<int> {
217  co_await folly::coro::Baton{true};
218  throw MyException{};
219  };
221 }
222 
223 TEST(InlineTask, ExceptionsPropagateFromRefTask) {
224  auto f = []() -> InlineTask<int&> {
225  co_await folly::coro::Baton{true};
226  throw MyException{};
227  };
229 }
230 
231 struct ThrowingCopyConstructor {
232  ThrowingCopyConstructor() noexcept = default;
233 
234  [[noreturn]] ThrowingCopyConstructor(const ThrowingCopyConstructor&) noexcept(
235  false) {
236  throw MyException{};
237  }
238 
239  ThrowingCopyConstructor& operator=(const ThrowingCopyConstructor&) = delete;
240 };
241 
242 TEST(InlineTask, ExceptionsPropagateFromReturnValueConstructor) {
243  auto f = []() -> InlineTask<ThrowingCopyConstructor> { co_return{}; };
245 }
246 
247 InlineTask<void> recursiveTask(int depth) {
248  if (depth > 0) {
249  co_await recursiveTask(depth - 1);
250  }
251 }
252 
253 TEST(InlineTask, DeepRecursionDoesntStackOverflow) {
254  folly::coro::blockingWait(recursiveTask(500000));
255 }
256 
257 InlineTask<int> recursiveValueTask(int depth) {
258  if (depth > 0) {
259  co_return co_await recursiveValueTask(depth - 1) + 1;
260  }
261  co_return 0;
262 }
263 
264 TEST(InlineTask, DeepRecursionOfValueTaskDoesntStackOverflow) {
265  EXPECT_EQ(500000, folly::coro::blockingWait(recursiveValueTask(500000)));
266 }
267 
268 InlineTask<void> recursiveThrowingTask(int depth) {
269  if (depth > 0) {
270  co_await recursiveThrowingTask(depth - 1);
271  }
272 
273  throw MyException{};
274 }
275 
276 TEST(InlineTask, DeepRecursionOfExceptions) {
277  EXPECT_THROW(
278  folly::coro::blockingWait(recursiveThrowingTask(50000)), MyException);
279 }
280 
281 #endif
Definition: InvokeTest.cpp:58
#define TEST(test_case_name, test_name)
Definition: gtest.h:2187
auto f
#define EXPECT_THROW(statement, expected_exception)
Definition: gtest.h:1843
std::string s_
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
STL namespace.
requires E e noexcept(noexcept(s.error(std::move(e))))
static const char *const value
Definition: Conv.cpp:50
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
T exchange(T &obj, U &&new_value)
Definition: Utility.h:120
const char * string
Definition: Conv.cpp:212
g_t g(f_t)
static set< string > s
const
Definition: upload.py:398
#define EXPECT_FALSE(condition)
Definition: gtest.h:1862
int x_
default
Definition: upload.py:394
auto blockingWait(Awaitable &&awaitable) -> detail::decay_rvalue_reference_t< await_result_t< Awaitable >>
Definition: BlockingWait.h:313