this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "gtest/gtest.h"
3
4#include "objects.h"
5#include "runtime.h"
6#include "test-utils.h"
7
8namespace py {
9namespace testing {
10
11using CoroutineTest = RuntimeFixture;
12using GeneratorTest = RuntimeFixture;
13using AsyncGeneratorTest = RuntimeFixture;
14
15TEST_F(GeneratorTest, Basic) {
16 const char* src = R"(
17def fib(n):
18 a = 0
19 b = 1
20 for i in range(n):
21 yield a
22 a, b = a + b, a
23
24result = [i for i in fib(7)]
25)";
26
27 HandleScope scope(thread_);
28 ASSERT_FALSE(runFromCStr(runtime_, src).isError());
29 Object result(&scope, mainModuleAt(runtime_, "result"));
30 EXPECT_PYLIST_EQ(result, {0, 1, 1, 2, 3, 5, 8});
31}
32
33TEST_F(GeneratorTest, InitialSend) {
34 HandleScope scope(thread_);
35 ASSERT_FALSE(runFromCStr(runtime_, R"(
36def gen():
37 global value
38 value = 3
39 value += yield 0
40 yield 'dummy'
41
42g = gen()
43g.send(None)
44g.send(7)
45)")
46 .isError());
47 Object result(&scope, mainModuleAt(runtime_, "value"));
48 EXPECT_TRUE(isIntEqualsWord(*result, 10));
49}
50
51TEST_F(GeneratorTest, BadInitialSend) {
52 const char* src = R"(
53def gen():
54 yield 0
55gen().send(1)
56)";
57 EXPECT_TRUE(
58 raisedWithStr(runFromCStr(runtime_, src), LayoutId::kTypeError,
59 "can't send non-None value to a just-started generator"));
60}
61
62TEST_F(GeneratorTest, YieldFrom) {
63 HandleScope scope(thread_);
64 ASSERT_FALSE(runFromCStr(runtime_, R"(
65result = []
66def log(obj):
67 global result
68 result.append(obj)
69
70def str_maker(l):
71 while True:
72 val = yield l
73 if val is None:
74 break
75 l += ' ' + val
76 yield from range(5)
77 return 'finished!'
78
79def g1():
80 start = yield 'ready'
81 x = yield from str_maker(start)
82 log(x)
83
84g = g1()
85log('priming')
86log(g.__next__())
87log('sending')
88initial_str = 'initial string'
89log(g.send(initial_str))
90log(g.send('first'))
91log(g.send('second'))
92log(g.send(None))
93for i in g:
94 log(i)
95)")
96 .isError());
97 Object result(&scope, mainModuleAt(runtime_, "result"));
98 EXPECT_PYLIST_EQ(
99 result,
100 {"priming", "ready", "sending", "initial string", "initial string first",
101 "initial string first second", 0, 1, 2, 3, 4, "finished!"});
102
103 // Manually check element 3 for object identity
104 ASSERT_TRUE(result.isList());
105 List list(&scope, *result);
106 Object initial(&scope, mainModuleAt(runtime_, "initial_str"));
107 EXPECT_GE(list.numItems(), 3);
108 EXPECT_EQ(list.at(3), *initial);
109}
110
111TEST_F(GeneratorTest, ReraiseAfterYield) {
112 EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
113def gen():
114 try:
115 raise RuntimeError("inside generator")
116 except:
117 yield
118 raise
119
120g = gen()
121g.__next__()
122try:
123 raise RuntimeError("outside generator")
124except:
125 g.__next__()
126)"),
127 LayoutId::kRuntimeError, "inside generator"));
128}
129
130TEST_F(GeneratorTest, ReturnFromTrySkipsExcept) {
131 HandleScope scope(thread_);
132 ASSERT_FALSE(runFromCStr(runtime_, R"(
133result = 0
134
135def gen():
136 global result
137 yield 0
138 try:
139 return 123
140 except:
141 result = -1
142 yield 1
143
144g = gen()
145g.__next__()
146try:
147 g.__next__()
148except StopIteration:
149 result = 1
150)")
151 .isError());
152
153 Object result(&scope, mainModuleAt(runtime_, "result"));
154 ASSERT_TRUE(result.isSmallInt());
155 EXPECT_EQ(SmallInt::cast(*result).value(), 1);
156}
157
158TEST_F(GeneratorTest, NextAfterReturnRaisesStopIteration) {
159 EXPECT_EQ(runFromCStr(runtime_, R"(
160def gen():
161 yield 0
162 return "hello there"
163
164g = gen()
165g.__next__()
166)"),
167 NoneType::object());
168 EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, "g.__next__()"),
169 LayoutId::kStopIteration, "hello there"));
170 thread_->clearPendingException();
171 EXPECT_TRUE(
172 raised(runFromCStr(runtime_, "g.__next__()"), LayoutId::kStopIteration));
173 thread_->clearPendingException();
174 EXPECT_TRUE(
175 raised(runFromCStr(runtime_, "g.__next__()"), LayoutId::kStopIteration));
176}
177
178TEST_F(GeneratorTest, NextAfterRaiseRaisesStopIteration) {
179 EXPECT_FALSE(runFromCStr(runtime_, R"(
180def gen():
181 yield 0
182 raise RuntimeError("kaboom")
183 yield 1
184
185g = gen()
186g.__next__()
187)")
188 .isError());
189 EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, "g.__next__()"),
190 LayoutId::kRuntimeError, "kaboom"));
191 thread_->clearPendingException();
192 EXPECT_TRUE(
193 raised(runFromCStr(runtime_, "g.__next__()"), LayoutId::kStopIteration));
194}
195
196TEST_F(CoroutineTest, Basic) {
197 HandleScope scope(thread_);
198 ASSERT_FALSE(runFromCStr(runtime_, R"(
199async def coro():
200 return 24
201c = coro()
202)")
203 .isError());
204 Object result(&scope, mainModuleAt(runtime_, "c"));
205 EXPECT_TRUE(result.isCoroutine());
206}
207
208TEST_F(CoroutineTest, BadInitialSend) {
209 const char* src = R"(
210async def coro():
211 return 0
212coro().send(1)
213)";
214 EXPECT_TRUE(
215 raisedWithStr(runFromCStr(runtime_, src), LayoutId::kTypeError,
216 "can't send non-None value to a just-started coroutine"));
217}
218
219TEST_F(AsyncGeneratorTest, Create) {
220 HandleScope scope(thread_);
221 ASSERT_FALSE(runFromCStr(runtime_, R"(
222async def async_gen():
223 yield 1234
224ag = async_gen()
225)")
226 .isError());
227 Object result(&scope, mainModuleAt(runtime_, "ag"));
228 EXPECT_TRUE(result.isAsyncGenerator());
229}
230
231} // namespace testing
232} // namespace py