this repo has no description
at trunk 388 lines 13 kB view raw
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