1//
2// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7
8#include <boost/cobalt/generator.hpp>
9#include <boost/cobalt/promise.hpp>
10#include <boost/cobalt/race.hpp>
11#include <boost/cobalt/op.hpp>
12#include <boost/core/ignore_unused.hpp>
13
14#include <boost/asio/steady_timer.hpp>
15
16#include <boost/test/unit_test.hpp>
17#include "test.hpp"
18
19using namespace boost;
20
21BOOST_AUTO_TEST_SUITE(generator);
22
23cobalt::generator<int> gen()
24{
25 for (int i = 0; i <10; i ++)
26 co_yield ret&: i;
27
28 co_return 10;
29}
30
31
32
33CO_TEST_CASE(generator_int)
34{
35 auto g = gen();
36 int i = 0;
37 {
38 auto aw = g.operator co_await();
39 BOOST_CHECK(aw.await_ready());
40 BOOST_CHECK(i ++ == co_await std::move(aw));
41 }
42
43 while (g)
44 BOOST_CHECK(i ++ == co_await g);
45
46
47
48 BOOST_CHECK(i == 11);
49
50 co_return ;
51}
52
53
54cobalt::generator<int, int> gen_push()
55{
56 int val = 1u;
57 for (int i = 0; i < 10; i++)
58 {
59 auto v = co_yield ret&: val;
60 BOOST_CHECK(v == val);
61 val += v;
62 }
63
64
65 co_return val;
66}
67
68
69CO_TEST_CASE(generator_push)
70{
71 auto g = gen_push();
72
73 int i = 1;
74 int nw = 1;
75 while (g)
76 {
77 nw = co_await g(i);
78 BOOST_CHECK(i == nw);
79 i *= 2;
80 }
81
82 BOOST_CHECK(i == 2048);
83 co_return ;
84}
85
86cobalt::generator<int> delay_gen(std::chrono::milliseconds tick)
87{
88 asio::steady_timer tim{co_await cobalt::this_coro::executor, std::chrono::steady_clock::now()};
89 for (int i = 0; i < 10; i ++)
90 {
91 co_await tim.async_wait(token: cobalt::use_op);
92 tim.expires_at(expiry_time: tim.expiry() + tick);
93 co_yield ret&: i;
94 }
95 co_return 10;
96}
97
98cobalt::generator<int> lazy_delay_gen(std::chrono::milliseconds tick)
99{
100 co_await cobalt::this_coro::initial;
101 asio::steady_timer tim{co_await cobalt::this_coro::executor, std::chrono::steady_clock::now()};
102 for (int i = 0; i < 10; i ++)
103 {
104 co_await tim.async_wait(token: cobalt::use_op);
105 tim.expires_at(expiry_time: tim.expiry() + tick);
106 co_yield ret&: i;
107 }
108 co_return 10;
109}
110
111
112#if !defined(BOOST_COBALT_NO_SELF_DELETE)
113
114CO_TEST_CASE(generator_left_race)
115{
116 asio::steady_timer tim{co_await cobalt::this_coro::executor, std::chrono::milliseconds(50)};
117 auto g1 = delay_gen(tick: std::chrono::milliseconds(200));
118 co_await tim.async_wait(token: cobalt::use_op);
119 auto g2 = delay_gen(tick: std::chrono::milliseconds(100));
120
121 using v = variant2::variant<int, int>;
122 auto v1 = [](int value) -> v {return v{variant2::in_place_index<0u>, value};};
123 auto v2 = [](int value) -> v {return v{variant2::in_place_index<1u>, value};};
124
125 BOOST_CHECK(v1(0) == co_await left_race(g1, g2));
126 BOOST_CHECK(v2(0) == co_await left_race(g1, g2));
127 BOOST_CHECK(v2(1) == co_await left_race(g1, g2));
128 BOOST_CHECK(v1(1) == co_await left_race(g1, g2));
129 BOOST_CHECK(v2(2) == co_await left_race(g1, g2));
130 BOOST_CHECK(v2(3) == co_await left_race(g1, g2));
131 BOOST_CHECK(v1(2) == co_await left_race(g1, g2));
132 BOOST_CHECK(v2(4) == co_await left_race(g1, g2));
133 BOOST_CHECK(v2(5) == co_await left_race(g1, g2));
134 BOOST_CHECK(v1(3) == co_await left_race(g1, g2));
135 BOOST_CHECK(v2(6) == co_await left_race(g1, g2));
136 BOOST_CHECK(v2(7) == co_await left_race(g1, g2));
137 BOOST_CHECK(v1(4) == co_await left_race(g1, g2));
138 BOOST_CHECK(v2(8) == co_await left_race(g1, g2));
139 BOOST_CHECK(v2(9) == co_await left_race(g1, g2));
140 BOOST_CHECK(v2(10) == co_await left_race(g1, g2));
141
142
143 BOOST_CHECK(!g2);
144 g1.cancel();
145 BOOST_CHECK_THROW(co_await g1, boost::system::system_error);
146}
147
148
149CO_TEST_CASE(lazy_generator_left_race)
150{
151 asio::steady_timer tim{co_await cobalt::this_coro::executor, std::chrono::milliseconds(50)};
152 auto g1 = lazy_delay_gen(tick: std::chrono::milliseconds(200));
153 co_await tim.async_wait(token: cobalt::use_op);
154 auto g2 = lazy_delay_gen(tick: std::chrono::milliseconds(100));
155
156 using v = variant2::variant<int, int>;
157 auto v1 = [](int value) -> v {return v{variant2::in_place_index<0u>, value};};
158 auto v2 = [](int value) -> v {return v{variant2::in_place_index<1u>, value};};
159
160 BOOST_CHECK(v1(0) == co_await left_race(g1, g2));
161 BOOST_CHECK(v2(0) == co_await left_race(g1, g2));
162 BOOST_CHECK(v2(1) == co_await left_race(g1, g2));
163 BOOST_CHECK(v2(2) == co_await left_race(g1, g2));
164 BOOST_CHECK(v1(1) == co_await left_race(g1, g2));
165 BOOST_CHECK(v2(3) == co_await left_race(g1, g2));
166 BOOST_CHECK(v2(4) == co_await left_race(g1, g2));
167 BOOST_CHECK(v1(2) == co_await left_race(g1, g2));
168 BOOST_CHECK(v2(5) == co_await left_race(g1, g2));
169 BOOST_CHECK(v2(6) == co_await left_race(g1, g2));
170 BOOST_CHECK(v1(3) == co_await left_race(g1, g2));
171 BOOST_CHECK(v2(7) == co_await left_race(g1, g2));
172 BOOST_CHECK(v2(8) == co_await left_race(g1, g2));
173 BOOST_CHECK(v1(4) == co_await left_race(g1, g2));
174 BOOST_CHECK(v2(9) == co_await left_race(g1, g2));
175 BOOST_CHECK(v2(10) == co_await left_race(g1, g2));
176
177
178 BOOST_CHECK(!g2);
179 g1.cancel();
180 BOOST_CHECK_THROW(co_await g1, boost::system::system_error);
181}
182
183#endif
184
185cobalt::generator<int> gshould_unwind(asio::io_context & ctx)
186{
187 co_await asio::post(ctx, token: cobalt::use_op);
188 co_return 0;
189}
190
191BOOST_AUTO_TEST_CASE(unwind)
192{
193 asio::io_context ctx;
194 boost::cobalt::this_thread::set_executor(ctx.get_executor());
195 boost::ignore_unused(gshould_unwind(ctx));
196}
197
198
199cobalt::generator<int> gen_stop()
200{
201 int val = 1u;
202 for (int i = 0; i < 10; i++)
203 {
204 if (i == 4)
205 co_await stop();
206 co_yield ret&: i;
207
208 }
209 co_return val;
210}
211
212#if !defined(BOOST_COBALT_USE_BOOST_CONTAINER_PMR)
213// clang-14 does not like this test.
214
215CO_TEST_CASE(stop)
216{
217 auto g = gen_stop();
218 while (g)
219 co_await g;
220
221 auto gg = std::move(g);
222}
223
224#endif
225
226cobalt::generator<int, int> eager()
227{
228 int i = co_await cobalt::this_coro::initial;
229 for (; i < 10; i += co_yield ret&: i);
230
231 co_return i;
232}
233
234
235CO_TEST_CASE(eager_2)
236{
237 auto g = eager();
238
239
240 BOOST_CHECK(2 == co_await g(2));
241 BOOST_CHECK(6 == co_await g(4));
242 BOOST_CHECK(9 == co_await g(3));
243
244 auto gg = std::move(g);
245// BOOST_CHECK(!g);
246 BOOST_CHECK(gg);
247 BOOST_CHECK(15 == co_await gg(6));
248 BOOST_CHECK(!gg);
249
250}
251
252struct generator_move_only
253{
254 generator_move_only() = default;
255 generator_move_only(generator_move_only &&) = default;
256 generator_move_only & operator=(generator_move_only &&) = delete;
257};
258
259cobalt::generator<generator_move_only, generator_move_only> gen_move_only_test()
260{
261 co_yield ret: generator_move_only{};
262 co_return generator_move_only{};
263}
264
265CO_TEST_CASE(move_only)
266{
267 auto g = gen_move_only_test();
268
269 co_await g(generator_move_only{});
270 co_await g(generator_move_only{});
271}
272
273cobalt::generator<int> detached()
274{
275 co_await asio::post(token: cobalt::use_op);
276 co_yield ret: 42;
277 co_return 24;
278}
279
280CO_TEST_CASE(detached_)
281{
282 boost::ignore_unused(detached());
283 co_return;
284}
285
286cobalt::generator<int, int> detached_push()
287{
288 int i = co_yield ret: 42;
289 while (true)
290 i = co_yield ret&: i;
291 co_return i;
292}
293
294CO_TEST_CASE(detached_push_)
295{
296 auto g = detached_push();
297
298 co_await g(1);
299}
300
301BOOST_AUTO_TEST_SUITE_END();

source code of boost/libs/cobalt/test/generator.cpp