this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "Python.h"
3#include "gtest/gtest.h"
4
5#include "capi-fixture.h"
6#include "capi-testing.h"
7
8namespace py {
9namespace testing {
10
11using StructSeqExtensionApiTest = ExtensionApi;
12
13PyStructSequence_Field desc_fields[] = {
14 {"first", "first field"}, {"second", "second field"},
15 {"third", "third field"}, {"fourth", "fourth field"},
16 {"fifth", "fifth field"}, {nullptr}};
17
18PyStructSequence_Desc desc = {"foo.bar", "docs", desc_fields, 2};
19
20TEST_F(StructSeqExtensionApiTest, NewTypeCreatesRuntimeType) {
21 PyObjectPtr type(PyStructSequence_NewType(&desc));
22 ASSERT_NE(type, nullptr);
23 ASSERT_EQ(PyErr_Occurred(), nullptr);
24 ASSERT_TRUE(PyType_CheckExact(type));
25
26 PyObjectPtr module(PyObject_GetAttrString(type, "__module__"));
27 EXPECT_TRUE(isUnicodeEqualsCStr(module, "foo"));
28 PyObjectPtr name(PyObject_GetAttrString(type, "__name__"));
29 EXPECT_TRUE(isUnicodeEqualsCStr(name, "bar"));
30 PyObjectPtr qualname(PyObject_GetAttrString(type, "__qualname__"));
31 EXPECT_TRUE(isUnicodeEqualsCStr(qualname, "bar"));
32
33 PyObjectPtr seq_attr1(PyObject_GetAttrString(type, "n_sequence_fields"));
34 ASSERT_EQ(PyErr_Occurred(), nullptr);
35 ASSERT_NE(seq_attr1, nullptr);
36 EXPECT_EQ(PyLong_AsLong(seq_attr1), 2);
37
38 PyObjectPtr seq_attr2(PyObject_GetAttrString(type, "n_unnamed_fields"));
39 ASSERT_NE(seq_attr2, nullptr);
40 EXPECT_EQ(PyLong_AsLong(seq_attr2), 0);
41
42 PyObjectPtr seq_attr3(PyObject_GetAttrString(type, "n_fields"));
43 ASSERT_NE(seq_attr3, nullptr);
44 EXPECT_EQ(PyLong_AsLong(seq_attr3), 5);
45}
46
47TEST_F(StructSeqExtensionApiTest, NewTypeWithUnnamedFieldsReturnsType) {
48 PyStructSequence_Field fields[] = {
49 {const_cast<char*>("foo"), const_cast<char*>("foo docu")},
50 {PyStructSequence_UnnamedField, const_cast<char*>("unnamed docu")},
51 {const_cast<char*>("bar"), const_cast<char*>("bar docu")},
52 {PyStructSequence_UnnamedField, const_cast<char*>("unnamed docu")},
53 {const_cast<char*>("baz"), const_cast<char*>("baz docu")},
54 {nullptr}};
55 PyStructSequence_Desc s_desc = {const_cast<char*>("S"),
56 const_cast<char*>("S docu"), fields, 4};
57 PyObjectPtr type(PyStructSequence_NewType(&s_desc));
58 ASSERT_NE(type, nullptr);
59 ASSERT_EQ(PyErr_Occurred(), nullptr);
60 ASSERT_TRUE(PyType_CheckExact(type));
61
62 PyObjectPtr n_unnamed_fields(
63 PyObject_GetAttrString(type, "n_unnamed_fields"));
64 EXPECT_TRUE(isLongEqualsLong(n_unnamed_fields, 2));
65 PyObjectPtr n_fields(PyObject_GetAttrString(type, "n_fields"));
66 EXPECT_TRUE(isLongEqualsLong(n_fields, 5));
67 PyObjectPtr n_sequence_fields(
68 PyObject_GetAttrString(type, "n_sequence_fields"));
69 EXPECT_TRUE(isLongEqualsLong(n_sequence_fields, 4));
70}
71
72TEST_F(StructSeqExtensionApiTest, SETITEMOnlyDecrefsOnce) {
73 PyObjectPtr type(PyStructSequence_NewType(&desc));
74 PyObjectPtr seq(PyStructSequence_New(type.asTypeObject()));
75 PyObject* value = PyUnicode_FromString("my_unique_string");
76 Py_ssize_t refcnt = Py_REFCNT(value);
77 PyStructSequence_SET_ITEM(seq.get(), 0, value);
78 // Pyro will have refcount of 1 less than CPython
79 EXPECT_LE(Py_REFCNT(value), refcnt);
80}
81
82TEST_F(StructSeqExtensionApiTest, GetItem) {
83 PyObjectPtr type(PyStructSequence_NewType(&desc));
84 ASSERT_NE(type, nullptr);
85 ASSERT_EQ(PyErr_Occurred(), nullptr);
86 ASSERT_TRUE(PyType_CheckExact(type));
87
88 ASSERT_EQ(moduleSet("__main__", "Structseq", type), 0);
89 PyRun_SimpleString(R"(
90result = Structseq((1,2))
91)");
92 PyObjectPtr result(mainModuleGet("result"));
93 ASSERT_EQ(PyErr_Occurred(), nullptr);
94 EXPECT_TRUE(PyTuple_Check(result));
95
96 PyObject* value = PyStructSequence_GetItem(result, 1);
97 ASSERT_TRUE(PyLong_Check(value));
98 EXPECT_EQ(PyLong_AsLong(value), 2);
99
100 PyObjectPtr value2(PyObject_GetAttrString(result, "second"));
101 ASSERT_TRUE(PyLong_Check(value2));
102 EXPECT_EQ(PyLong_AsLong(value2), 2);
103
104 EXPECT_EQ(value, value2);
105}
106
107TEST_F(StructSeqExtensionApiTest, GetItemWithIndexReturnsValue) {
108 PyObjectPtr type(PyStructSequence_NewType(&desc));
109 ASSERT_NE(type, nullptr);
110 ASSERT_EQ(PyErr_Occurred(), nullptr);
111 ASSERT_TRUE(PyType_CheckExact(type));
112
113 ASSERT_EQ(moduleSet("__main__", "Structseq", type), 0);
114 PyRun_SimpleString(R"(
115result = Structseq((1,2))[0]
116)");
117 PyObjectPtr result(mainModuleGet("result"));
118 ASSERT_EQ(PyErr_Occurred(), nullptr);
119 ASSERT_TRUE(PyLong_Check(result));
120 EXPECT_EQ(PyLong_AsLong(result), 1);
121}
122
123TEST_F(StructSeqExtensionApiTest,
124 GetItemWithIndexToHiddenValueRaisesException) {
125 PyObjectPtr type(PyStructSequence_NewType(&desc));
126 ASSERT_NE(type, nullptr);
127 ASSERT_EQ(PyErr_Occurred(), nullptr);
128 ASSERT_TRUE(PyType_CheckExact(type));
129
130 ASSERT_EQ(moduleSet("__main__", "Structseq", type), 0);
131 // TODO(T40700664): Use PyRun_String and test for raised exception
132 EXPECT_EQ(PyRun_SimpleString(R"(
133import sys
134sys.excepthook = lambda *args: None
135Structseq((1,2,3))[2]
136)"),
137 -1);
138}
139
140TEST_F(StructSeqExtensionApiTest, GetItemWithNameReturnsValue) {
141 PyObjectPtr type(PyStructSequence_NewType(&desc));
142 ASSERT_NE(type, nullptr);
143 ASSERT_EQ(PyErr_Occurred(), nullptr);
144 ASSERT_TRUE(PyType_CheckExact(type));
145
146 ASSERT_EQ(moduleSet("__main__", "Structseq", type), 0);
147 PyRun_SimpleString(R"(
148result = Structseq((1,2)).first
149)");
150 PyObjectPtr result(mainModuleGet("result"));
151 ASSERT_EQ(PyErr_Occurred(), nullptr);
152 ASSERT_TRUE(PyLong_Check(result));
153 EXPECT_EQ(PyLong_AsLong(result), 1);
154}
155
156TEST_F(StructSeqExtensionApiTest, GetItemWithNameToHiddenValueReturnsValue) {
157 PyObjectPtr type(PyStructSequence_NewType(&desc));
158 ASSERT_NE(type, nullptr);
159 ASSERT_EQ(PyErr_Occurred(), nullptr);
160 ASSERT_TRUE(PyType_CheckExact(type));
161
162 ASSERT_EQ(moduleSet("__main__", "Structseq", type), 0);
163 PyRun_SimpleString(R"(
164result = Structseq((1,2,3)).third
165)");
166 PyObjectPtr result(mainModuleGet("result"));
167 ASSERT_EQ(PyErr_Occurred(), nullptr);
168 ASSERT_TRUE(PyLong_Check(result));
169 EXPECT_EQ(PyLong_AsLong(result), 3);
170}
171
172TEST_F(StructSeqExtensionApiTest,
173 GetItemWithNameToUnsetHiddenValueReturnsValue) {
174 PyObjectPtr type(PyStructSequence_NewType(&desc));
175 ASSERT_NE(type, nullptr);
176 ASSERT_EQ(PyErr_Occurred(), nullptr);
177 ASSERT_TRUE(PyType_CheckExact(type));
178
179 ASSERT_EQ(moduleSet("__main__", "Structseq", type), 0);
180 PyRun_SimpleString(R"(
181result = Structseq((1,2,3)).fifth
182)");
183 PyObjectPtr result(mainModuleGet("result"));
184 ASSERT_EQ(PyErr_Occurred(), nullptr);
185 EXPECT_EQ(result, Py_None);
186}
187
188TEST_F(StructSeqExtensionApiTest, GetItemWithDictAndInvalidFieldReturnsValue) {
189 PyObjectPtr type(PyStructSequence_NewType(&desc));
190 ASSERT_NE(type, nullptr);
191 ASSERT_EQ(PyErr_Occurred(), nullptr);
192 ASSERT_TRUE(PyType_CheckExact(type));
193
194 ASSERT_EQ(moduleSet("__main__", "Structseq", type), 0);
195 PyRun_SimpleString(R"(
196result = Structseq((1,2), {"badattr": 3}).first
197)");
198 PyObjectPtr result(mainModuleGet("result"));
199 ASSERT_TRUE(PyLong_Check(result));
200 EXPECT_EQ(PyLong_AsLong(result), 1);
201}
202
203TEST_F(StructSeqExtensionApiTest,
204 GetItemFromDictWithInvalidFieldRaisesException) {
205 PyObjectPtr type(PyStructSequence_NewType(&desc));
206 ASSERT_NE(type, nullptr);
207 ASSERT_EQ(PyErr_Occurred(), nullptr);
208 ASSERT_TRUE(PyType_CheckExact(type));
209
210 ASSERT_EQ(moduleSet("__main__", "Structseq", type), 0);
211 // TODO(T40700664): Use PyRun_String and test for raised exception
212 EXPECT_EQ(PyRun_SimpleString(R"(
213import sys
214sys.excepthook = lambda *args: None
215Structseq((1,2), {"badattr": 3}).badattr
216)"),
217 -1);
218}
219
220TEST_F(StructSeqExtensionApiTest, LenReturnsVisibleSize) {
221 PyObjectPtr type(PyStructSequence_NewType(&desc));
222 ASSERT_NE(type, nullptr);
223 ASSERT_EQ(PyErr_Occurred(), nullptr);
224 ASSERT_TRUE(PyType_CheckExact(type));
225
226 ASSERT_EQ(moduleSet("__main__", "Structseq", type), 0);
227 PyRun_SimpleString(R"(
228result = len(Structseq((1,2,3)))
229)");
230 PyObjectPtr result(mainModuleGet("result"));
231 ASSERT_EQ(PyErr_Occurred(), nullptr);
232 ASSERT_TRUE(PyLong_Check(result));
233 EXPECT_EQ(PyLong_AsLong(result), 2);
234}
235
236TEST_F(StructSeqExtensionApiTest, IterReturnsVisibleItems) {
237 PyObjectPtr type(PyStructSequence_NewType(&desc));
238 ASSERT_NE(type, nullptr);
239 ASSERT_EQ(PyErr_Occurred(), nullptr);
240 ASSERT_TRUE(PyType_CheckExact(type));
241
242 ASSERT_EQ(moduleSet("__main__", "Structseq", type), 0);
243 PyRun_SimpleString(R"(
244structseq = Structseq((1,2,3,4,5))
245result = [x for x in structseq]
246)");
247 PyObjectPtr result(mainModuleGet("result"));
248 ASSERT_EQ(PyErr_Occurred(), nullptr);
249 ASSERT_TRUE(PyList_Check(result));
250 EXPECT_EQ(PyList_Size(result), 2);
251 EXPECT_EQ(PyLong_AsLong(PyList_GetItem(result, 0)), 1);
252 EXPECT_EQ(PyLong_AsLong(PyList_GetItem(result, 1)), 2);
253}
254
255TEST_F(StructSeqExtensionApiTest, SetItemRaisesException) {
256 PyObjectPtr type(PyStructSequence_NewType(&desc));
257 ASSERT_NE(type, nullptr);
258 ASSERT_EQ(PyErr_Occurred(), nullptr);
259 ASSERT_TRUE(PyType_CheckExact(type));
260
261 ASSERT_EQ(moduleSet("__main__", "Structseq", type), 0);
262 // TODO(T40700664): Use PyRun_String and test for raised exception
263 EXPECT_EQ(PyRun_SimpleString(R"(
264import sys
265sys.excepthook = lambda *args: None
266structseq = Structseq((1,2,3))
267structseq.first = 4
268)"),
269 -1);
270}
271
272TEST_F(StructSeqExtensionApiTest, TupleSizeReturnsVisibleSize) {
273 PyObjectPtr type(PyStructSequence_NewType(&desc));
274 ASSERT_NE(type, nullptr);
275 ASSERT_EQ(PyErr_Occurred(), nullptr);
276 ASSERT_TRUE(PyType_CheckExact(type));
277
278 PyObjectPtr instance(PyStructSequence_New(type.asTypeObject()));
279 ASSERT_TRUE(PyTuple_Check(instance));
280 EXPECT_EQ(PyTuple_Size(instance), 2);
281}
282
283TEST_F(StructSeqExtensionApiTest, GetItemReturnsValue) {
284 PyObjectPtr type(PyStructSequence_NewType(&desc));
285 ASSERT_NE(type, nullptr);
286 ASSERT_EQ(PyErr_Occurred(), nullptr);
287 ASSERT_TRUE(PyType_CheckExact(type));
288
289 PyObjectPtr instance(PyStructSequence_New(type.asTypeObject()));
290 ASSERT_TRUE(PyTuple_Check(instance));
291
292 PyObject* value = PyLong_FromLong(123); // reference will be stolen
293 EXPECT_EQ(PyStructSequence_SET_ITEM(instance.get(), 0, value), value);
294 ASSERT_EQ(PyErr_Occurred(), nullptr);
295
296 PyObject* result = PyStructSequence_GET_ITEM(instance.get(), 0);
297 ASSERT_EQ(PyErr_Occurred(), nullptr);
298 EXPECT_EQ(PyLong_AsLong(result), 123);
299}
300
301TEST_F(StructSeqExtensionApiTest,
302 GetItemFromUninitializedFieldReturnsNonePyro) {
303 // Pyro only test as CPython initializes these to nullptr
304 PyObjectPtr type(PyStructSequence_NewType(&desc));
305 ASSERT_NE(type, nullptr);
306 ASSERT_EQ(PyErr_Occurred(), nullptr);
307 ASSERT_TRUE(PyType_CheckExact(type));
308
309 PyObjectPtr instance(PyStructSequence_New(type.asTypeObject()));
310 ASSERT_TRUE(PyTuple_Check(instance));
311
312 PyObject* result = PyStructSequence_GET_ITEM(instance.get(), 0);
313 ASSERT_EQ(PyErr_Occurred(), nullptr);
314 EXPECT_EQ(result, Py_None);
315}
316
317TEST_F(StructSeqExtensionApiTest, GetItemHiddenFieldReturnsValue) {
318 PyObjectPtr type(PyStructSequence_NewType(&desc));
319 ASSERT_NE(type, nullptr);
320 ASSERT_EQ(PyErr_Occurred(), nullptr);
321 ASSERT_TRUE(PyType_CheckExact(type));
322
323 PyObjectPtr instance(PyStructSequence_New(type.asTypeObject()));
324 ASSERT_TRUE(PyTuple_Check(instance));
325
326 PyStructSequence_SetItem(instance, 4, PyLong_FromLong(123));
327 ASSERT_EQ(PyErr_Occurred(), nullptr);
328
329 PyObject* result = PyStructSequence_GetItem(instance.get(), 4);
330 ASSERT_EQ(PyErr_Occurred(), nullptr);
331 EXPECT_EQ(PyLong_AsLong(result), 123);
332}
333
334TEST_F(StructSeqExtensionApiTest, GetNamedItemReturnsValue) {
335 PyObjectPtr type(PyStructSequence_NewType(&desc));
336 ASSERT_NE(type, nullptr);
337 ASSERT_EQ(PyErr_Occurred(), nullptr);
338 ASSERT_TRUE(PyType_CheckExact(type));
339
340 PyObjectPtr instance(PyStructSequence_New(type.asTypeObject()));
341 ASSERT_TRUE(PyTuple_Check(instance));
342
343 PyStructSequence_SetItem(instance, 0, PyLong_FromLong(123));
344 ASSERT_EQ(PyErr_Occurred(), nullptr);
345
346 PyObjectPtr result(PyObject_GetAttrString(instance, "first"));
347 ASSERT_EQ(PyErr_Occurred(), nullptr);
348 EXPECT_EQ(PyLong_AsLong(result), 123);
349}
350
351TEST_F(StructSeqExtensionApiTest,
352 GetNamedItemFromUninitializedFieldReturnsNone) {
353 PyObjectPtr type(PyStructSequence_NewType(&desc));
354 ASSERT_NE(type, nullptr);
355 ASSERT_EQ(PyErr_Occurred(), nullptr);
356 ASSERT_TRUE(PyType_CheckExact(type));
357
358 PyObjectPtr instance(PyStructSequence_New(type.asTypeObject()));
359 ASSERT_TRUE(PyTuple_Check(instance));
360
361 PyObjectPtr result(PyObject_GetAttrString(instance, "first"));
362 ASSERT_EQ(PyErr_Occurred(), nullptr);
363 EXPECT_EQ(result, Py_None);
364}
365
366TEST_F(StructSeqExtensionApiTest, GetSlotNewOnStructSeqReturnsSlot) {
367 PyObjectPtr type(PyStructSequence_NewType(&desc));
368 ASSERT_NE(type, nullptr);
369
370 auto slot_new =
371 reinterpret_cast<newfunc>(PyType_GetSlot(type.asTypeObject(), Py_tp_new));
372 ASSERT_NE(slot_new, nullptr);
373 PyObjectPtr tuple(PyTuple_New(3));
374 PyTuple_SetItem(tuple, 0, PyLong_FromLong(111));
375 PyTuple_SetItem(tuple, 1, PyLong_FromLong(222));
376 PyTuple_SetItem(tuple, 2, PyLong_FromLong(333));
377 PyObjectPtr args(PyTuple_Pack(1, tuple.get()));
378 PyObjectPtr seq(slot_new(type.asTypeObject(), args, nullptr));
379 ASSERT_NE(seq, nullptr);
380 ASSERT_EQ(PyObject_IsInstance(seq, type), 1);
381 EXPECT_TRUE(isLongEqualsLong(PyStructSequence_GetItem(seq, 0), 111));
382 EXPECT_TRUE(isLongEqualsLong(PyStructSequence_GetItem(seq, 1), 222));
383 PyObjectPtr third(PyObject_GetAttrString(seq, "third"));
384 EXPECT_TRUE(isLongEqualsLong(third, 333));
385}
386
387} // namespace testing
388} // namespace py