2 * Copyright (c) 2018 ARM Limited
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #include <gtest/gtest.h>
40 #include "base/coroutine.hh"
45 * This test is checking if the Coroutine, once it's created
46 * it doesn't start since the second argument of the constructor
47 * (run_coroutine) is set to false
49 TEST(Coroutine
, Unstarted
)
52 [] (Coroutine
<void, void>::CallerType
& yield
)
57 const bool start_upon_creation
= false;
58 Coroutine
<void, void> coro(yielding_task
, start_upon_creation
);
60 ASSERT_FALSE(coro
.started());
64 * This test is checking if the Coroutine, once it yields
65 * back to the caller, it is still marked as not finished.
67 TEST(Coroutine
, Unfinished
)
70 [] (Coroutine
<void, void>::CallerType
& yield
)
75 Coroutine
<void, void> coro(yielding_task
);
80 * This test is checking the parameter passing interface of a
81 * coroutine which takes an integer as an argument.
82 * Coroutine::operator() and CallerType::get() are the tested
85 TEST(Coroutine
, Passing
)
87 const std::vector
<int> input
{ 1, 2, 3 };
88 const std::vector
<int> expected_values
= input
;
91 [&expected_values
] (Coroutine
<int, void>::CallerType
& yield
)
95 for (const auto expected
: expected_values
) {
96 argument
= yield
.get();
97 ASSERT_EQ(argument
, expected
);
101 Coroutine
<int, void> coro(passing_task
);
104 for (const auto val
: input
) {
110 * This test is checking the yielding interface of a coroutine
111 * which takes no argument and returns integers.
112 * Coroutine::get() and CallerType::operator() are the tested
115 TEST(Coroutine
, Returning
)
117 const std::vector
<int> output
{ 1, 2, 3 };
118 const std::vector
<int> expected_values
= output
;
120 auto returning_task
=
121 [&output
] (Coroutine
<void, int>::CallerType
& yield
)
123 for (const auto ret
: output
) {
128 Coroutine
<void, int> coro(returning_task
);
131 for (const auto expected
: expected_values
) {
132 int returned
= coro
.get();
133 ASSERT_EQ(returned
, expected
);
138 * This test is still supposed to test the returning interface
139 * of the the Coroutine, proving how coroutine can be used
141 * The coroutine is computing the first #steps of the fibonacci
142 * sequence and it is yielding back results one number per time.
144 TEST(Coroutine
, Fibonacci
)
146 const std::vector
<int> expected_values
{
147 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233 };
149 const int steps
= expected_values
.size();
151 auto fibonacci_task
=
152 [steps
] (Coroutine
<void, int>::CallerType
& yield
)
157 for (auto iter
= 0; iter
< steps
; iter
++) {
158 int sum
= prev
+ current
;
166 Coroutine
<void, int> coro(fibonacci_task
);
169 for (const auto expected
: expected_values
) {
171 int returned
= coro
.get();
172 ASSERT_EQ(returned
, expected
);
177 * This test is using a bi-channel coroutine (accepting and
178 * yielding values) for testing a cooperative task.
179 * The caller and the coroutine have a string each; they are
180 * composing a new string by merging the strings together one
181 * character per time.
182 * The result string is hence passed back and forth between the
183 * coroutine and the caller.
185 TEST(Coroutine
, Cooperative
)
187 const std::string
caller_str("HloWrd");
188 const std::string
coro_str("el ol!");
189 const std::string
expected("Hello World!");
191 auto cooperative_task
=
192 [&coro_str
] (Coroutine
<std::string
, std::string
>::CallerType
& yield
)
194 for (auto& appended_c
: coro_str
) {
195 auto old_str
= yield
.get();
196 yield(old_str
+ appended_c
);
200 Coroutine
<std::string
, std::string
> coro(cooperative_task
);
203 for (auto& c
: caller_str
) {
206 result
= coro(result
).get();
209 ASSERT_EQ(result
, expected
);
213 * This test is testing nested coroutines by using one inner and one
214 * outer coroutine. It basically ensures that yielding from the inner
215 * coroutine returns to the outer coroutine (mid-layer of execution) and
216 * not to the outer caller.
218 TEST(Coroutine
, Nested
)
220 const std::string
wrong("Inner");
221 const std::string
expected("Inner + Outer");
224 [] (Coroutine
<void, std::string
>::CallerType
& yield
)
226 std::string
inner_string("Inner");
231 [&inner_task
] (Coroutine
<void, std::string
>::CallerType
& yield
)
233 Coroutine
<void, std::string
> coro(inner_task
);
234 std::string inner_string
= coro
.get();
236 std::string
outer_string("Outer");
237 yield(inner_string
+ " + " + outer_string
);
241 Coroutine
<void, std::string
> coro(outer_task
);
244 std::string result
= coro
.get();
246 ASSERT_NE(result
, wrong
);
247 ASSERT_EQ(result
, expected
);
251 * This test is stressing the scenario where two distinct fibers are
252 * calling the same coroutine. First the test instantiates (and runs) a
253 * coroutine, then spawns another one and it passes it a reference to
254 * the first coroutine. Once the new coroutine calls the first coroutine
255 * and the first coroutine yields, we are expecting execution flow to
256 * be yielded to the second caller (the second coroutine) and not the
257 * original caller (the test itself)
259 TEST(Coroutine
, TwoCallers
)
261 bool valid_return
= false;
263 Coroutine
<void, void> callee
{[]
264 (Coroutine
<void, void>::CallerType
& yield
)
270 Coroutine
<void, void> other_caller
{[&callee
, &valid_return
]
271 (Coroutine
<void, void>::CallerType
& yield
)
278 ASSERT_TRUE(valid_return
);