this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include <cfloat>
3#include <cmath>
4#include <cstring>
5
6#include "Python.h"
7#include "gtest/gtest.h"
8
9#include "capi-fixture.h"
10#include "capi-testing.h"
11
12namespace py {
13namespace testing {
14
15using FloatExtensionApiTest = ExtensionApi;
16
17TEST_F(FloatExtensionApiTest, PyFloatFromStringWithUnicodeReturnsFloat) {
18 const char* str = "15.5";
19 PyObjectPtr pyunicode(PyUnicode_FromString(str));
20 PyObjectPtr flt(PyFloat_FromString(pyunicode));
21 ASSERT_EQ(PyErr_Occurred(), nullptr);
22 ASSERT_TRUE(PyFloat_CheckExact(flt));
23 EXPECT_EQ(PyFloat_AsDouble(flt), 15.5);
24}
25
26TEST_F(FloatExtensionApiTest, PyFloatFromStringWithBytesReturnsFloat) {
27 const char* str = "25.5";
28 PyObjectPtr pybytes(PyBytes_FromString(str));
29 PyObjectPtr flt(PyFloat_FromString(pybytes));
30 ASSERT_EQ(PyErr_Occurred(), nullptr);
31 ASSERT_TRUE(PyFloat_CheckExact(flt));
32 EXPECT_EQ(PyFloat_AsDouble(flt), 25.5);
33}
34
35TEST_F(FloatExtensionApiTest, PyFloatFromStringWithMemoryViewReturnsFloat) {
36 const char* str = "5.5";
37 PyObjectPtr memory_view(
38 PyMemoryView_FromMemory(const_cast<char*>(str), 3, PyBUF_READ));
39 PyObjectPtr flt(PyFloat_FromString(memory_view));
40 ASSERT_EQ(PyErr_Occurred(), nullptr);
41 ASSERT_TRUE(PyFloat_CheckExact(flt));
42 EXPECT_EQ(PyFloat_AsDouble(flt), 5.5);
43}
44
45// TODO(T57022841): Needs PyFloatFromStringWithByteArrayReturnsFloat
46// when bytearray support is added to float()
47
48TEST_F(FloatExtensionApiTest, PyFloatFromStringWithUserBufferReturnsFloat) {
49 static char contents[] = "45.5";
50 static Py_ssize_t contents_len = std::strlen(contents);
51 getbufferproc mocked_getbuffer_func = [](PyObject* obj, Py_buffer* view,
52 int flags) {
53 return PyBuffer_FillInfo(view, obj, contents, contents_len, /*readonly=*/1,
54 flags);
55 };
56 releasebufferproc mocked_releasebuffer_func = [](PyObject*, Py_buffer* view) {
57 view->buf = nullptr;
58 view->obj = nullptr;
59 };
60 PyType_Slot slots[] = {
61 {Py_bf_getbuffer, reinterpret_cast<void*>(mocked_getbuffer_func)},
62 {Py_bf_releasebuffer, reinterpret_cast<void*>(mocked_releasebuffer_func)},
63 {0, nullptr},
64 };
65 static PyType_Spec spec;
66 spec = {
67 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
68 };
69 PyObjectPtr buf_type(PyType_FromSpec(&spec));
70 ASSERT_NE(buf_type, nullptr);
71 ASSERT_TRUE(PyType_CheckExact(buf_type));
72
73 PyObjectPtr instance(PyObject_CallFunction(buf_type, nullptr));
74 ASSERT_TRUE(PyObject_CheckBuffer(instance.get()));
75
76 PyObjectPtr flt(PyFloat_FromString(instance));
77 ASSERT_EQ(PyErr_Occurred(), nullptr);
78 ASSERT_TRUE(PyFloat_CheckExact(flt));
79 EXPECT_EQ(PyFloat_AsDouble(flt), 45.5);
80}
81
82TEST_F(FloatExtensionApiTest,
83 PyFloatFromStringWithUserBufferAndOnlyGetbufferReturnsFloat) {
84 static char contents[] = "45.5";
85 static Py_ssize_t contents_len = std::strlen(contents);
86 getbufferproc mocked_getbuffer_func = [](PyObject* obj, Py_buffer* view,
87 int flags) {
88 return PyBuffer_FillInfo(view, obj, contents, contents_len, /*readonly=*/1,
89 flags);
90 };
91 PyType_Slot slots[] = {
92 {Py_bf_getbuffer, reinterpret_cast<void*>(mocked_getbuffer_func)},
93 {0, nullptr},
94 };
95 static PyType_Spec spec;
96 spec = {
97 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
98 };
99 PyObjectPtr buf_type(PyType_FromSpec(&spec));
100 ASSERT_NE(buf_type, nullptr);
101 ASSERT_TRUE(PyType_CheckExact(buf_type));
102
103 PyObjectPtr instance(PyObject_CallFunction(buf_type, nullptr));
104 ASSERT_TRUE(PyObject_CheckBuffer(instance.get()));
105
106 PyObjectPtr flt(PyFloat_FromString(instance));
107 ASSERT_EQ(PyErr_Occurred(), nullptr);
108 ASSERT_TRUE(PyFloat_CheckExact(flt));
109 EXPECT_EQ(PyFloat_AsDouble(flt), 45.5);
110}
111
112TEST_F(FloatExtensionApiTest,
113 PyFloatFromStringWithNonNullTermUserBufferReturnsFloat) {
114 static char contents[] = "55.5 not null terminated";
115 static Py_ssize_t contents_len = 4; // Not the whole string
116 getbufferproc mocked_getbuffer_func = [](PyObject* obj, Py_buffer* view,
117 int flags) {
118 return PyBuffer_FillInfo(view, obj, contents, contents_len, /*readonly*/ 1,
119 flags);
120 };
121 releasebufferproc mocked_releasebuffer_func = [](PyObject*, Py_buffer* view) {
122 view->buf = nullptr;
123 view->obj = nullptr;
124 };
125 PyType_Slot slots[] = {
126 {Py_bf_getbuffer, reinterpret_cast<void*>(mocked_getbuffer_func)},
127 {Py_bf_releasebuffer, reinterpret_cast<void*>(mocked_releasebuffer_func)},
128 {0, nullptr},
129 };
130 static PyType_Spec spec;
131 spec = {
132 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
133 };
134 PyObjectPtr buf_type(PyType_FromSpec(&spec));
135 ASSERT_NE(buf_type, nullptr);
136 ASSERT_TRUE(PyType_CheckExact(buf_type));
137
138 PyObjectPtr instance(PyObject_CallFunction(buf_type, nullptr));
139 ASSERT_TRUE(PyObject_CheckBuffer(instance.get()));
140
141 PyObjectPtr flt(PyFloat_FromString(instance));
142 ASSERT_EQ(PyErr_Occurred(), nullptr);
143 ASSERT_TRUE(PyFloat_CheckExact(flt));
144 EXPECT_EQ(PyFloat_AsDouble(flt), 55.5);
145}
146
147TEST_F(FloatExtensionApiTest,
148 PyFloatFromStringWithEmbededNullTermUserBufferRaisesValueError) {
149 static char contents[] = "55.5\0 embeded null";
150 static Py_ssize_t contents_len = 18; // Include the null and more...
151 getbufferproc mocked_getbuffer_func = [](PyObject* obj, Py_buffer* view,
152 int flags) {
153 return PyBuffer_FillInfo(view, obj, contents, contents_len, /*readonly*/ 1,
154 flags);
155 };
156 releasebufferproc mocked_releasebuffer_func = [](PyObject*, Py_buffer* view) {
157 view->buf = nullptr;
158 view->obj = nullptr;
159 };
160 PyType_Slot slots[] = {
161 {Py_bf_getbuffer, reinterpret_cast<void*>(mocked_getbuffer_func)},
162 {Py_bf_releasebuffer, reinterpret_cast<void*>(mocked_releasebuffer_func)},
163 {0, nullptr},
164 };
165 static PyType_Spec spec;
166 spec = {
167 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
168 };
169 PyObjectPtr buf_type(PyType_FromSpec(&spec));
170 ASSERT_NE(buf_type, nullptr);
171 ASSERT_TRUE(PyType_CheckExact(buf_type));
172
173 PyObjectPtr instance(PyObject_CallFunction(buf_type, nullptr));
174 ASSERT_TRUE(PyObject_CheckBuffer(instance.get()));
175
176 PyObjectPtr flt(PyFloat_FromString(instance));
177 EXPECT_FALSE(flt);
178 ASSERT_NE(PyErr_Occurred(), nullptr);
179 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_ValueError));
180}
181
182TEST_F(FloatExtensionApiTest,
183 PyFloatFromStringWithUserBufferAndRaisingGetBufferRaisesTypeError) {
184 getbufferproc mocked_getbuffer_func = [](PyObject*, Py_buffer*, int) {
185 PyErr_Format(PyExc_NotImplementedError, "not implemented");
186 return -1;
187 };
188 releasebufferproc mocked_releasebuffer_func = [](PyObject*, Py_buffer* view) {
189 view->buf = nullptr;
190 view->obj = nullptr;
191 };
192 PyType_Slot slots[] = {
193 {Py_bf_getbuffer, reinterpret_cast<void*>(mocked_getbuffer_func)},
194 {Py_bf_releasebuffer, reinterpret_cast<void*>(mocked_releasebuffer_func)},
195 {0, nullptr},
196 };
197 static PyType_Spec spec;
198 spec = {
199 "foo.Bar", 0, 0, Py_TPFLAGS_DEFAULT, slots,
200 };
201 PyObjectPtr buf_type(PyType_FromSpec(&spec));
202 ASSERT_NE(buf_type, nullptr);
203 ASSERT_TRUE(PyType_CheckExact(buf_type));
204
205 PyObjectPtr instance(PyObject_CallFunction(buf_type, nullptr));
206 ASSERT_TRUE(PyObject_CheckBuffer(instance.get()));
207
208 PyObjectPtr flt(PyFloat_FromString(instance));
209 EXPECT_FALSE(flt);
210 ASSERT_NE(PyErr_Occurred(), nullptr);
211 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
212}
213
214TEST_F(FloatExtensionApiTest, PyFloatFromStringWithIntRaisesTypeError) {
215 PyObjectPtr integer(PyLong_FromLong(100));
216 EXPECT_EQ(PyFloat_FromString(integer), nullptr);
217 ASSERT_NE(PyErr_Occurred(), nullptr);
218 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
219}
220
221TEST_F(FloatExtensionApiTest, PyFloatFromStringWithFloatRaisesTypeError) {
222 PyObjectPtr flt(PyFloat_FromDouble(1.5));
223 EXPECT_EQ(PyFloat_FromString(flt), nullptr);
224 ASSERT_NE(PyErr_Occurred(), nullptr);
225 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
226}
227
228TEST_F(FloatExtensionApiTest, PyFloatFromStringWithNoneRaisesTypeError) {
229 EXPECT_EQ(PyFloat_FromString(Py_None), nullptr);
230 ASSERT_NE(PyErr_Occurred(), nullptr);
231 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
232}
233
234TEST_F(FloatExtensionApiTest, FromDoubleReturnsFloat) {
235 const double val = 15.4;
236 PyObjectPtr flt(PyFloat_FromDouble(val));
237 ASSERT_TRUE(PyFloat_CheckExact(flt));
238 EXPECT_EQ(PyFloat_AsDouble(flt), val);
239}
240
241TEST_F(FloatExtensionApiTest, NegativeFromDoubleReturnsFloat) {
242 const double val = -10000.123;
243 PyObjectPtr flt(PyFloat_FromDouble(val));
244 EXPECT_TRUE(PyFloat_CheckExact(flt));
245 EXPECT_EQ(PyFloat_AsDouble(flt), val);
246}
247
248TEST_F(FloatExtensionApiTest, AsDoubleFromNullRaisesException) {
249 double res = PyFloat_AsDouble(nullptr);
250 EXPECT_EQ(res, -1);
251
252 ASSERT_NE(PyErr_Occurred(), nullptr);
253 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
254}
255
256TEST_F(FloatExtensionApiTest, AsDoubleFromNonFloatRaisesException) {
257 PyObjectPtr list(PyList_New(0));
258 double res = PyFloat_AsDouble(list);
259 EXPECT_EQ(res, -1);
260
261 ASSERT_NE(PyErr_Occurred(), nullptr);
262 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_TypeError));
263}
264
265TEST_F(FloatExtensionApiTest, AsDoubleCallsDunderFloat) {
266 PyRun_SimpleString(R"(
267class FloatLikeClass:
268 def __float__(self):
269 return 1.5
270
271f = FloatLikeClass();
272 )");
273 PyObjectPtr f(testing::mainModuleGet("f"));
274 double res = PyFloat_AsDouble(f);
275 EXPECT_EQ(res, 1.5);
276}
277
278TEST_F(FloatExtensionApiTest, AsDoubleWithDunderFloatPropagatesException) {
279 PyRun_SimpleString(R"(
280class FloatLikeClass:
281 @property
282 def __float__(self):
283 raise KeyError
284
285f = FloatLikeClass();
286 )");
287 PyObjectPtr f(mainModuleGet("f"));
288 EXPECT_EQ(PyFloat_AsDouble(f), -1.0);
289
290 ASSERT_NE(PyErr_Occurred(), nullptr);
291 EXPECT_TRUE(PyErr_ExceptionMatches(PyExc_KeyError));
292}
293
294TEST_F(FloatExtensionApiTest, AsDoubleFromFloatSubclassReturnsFloat) {
295 PyRun_SimpleString(R"(
296class SubFloat(float):
297 def __new__(self, value):
298 self.foo = 3
299 return super().__new__(self, value)
300subfloat = SubFloat(1.5)
301subfloat_foo = subfloat.foo
302 )");
303 PyObjectPtr subfloat(testing::mainModuleGet("subfloat"));
304 ASSERT_FALSE(PyLong_CheckExact(subfloat));
305 ASSERT_TRUE(PyFloat_Check(subfloat));
306 EXPECT_EQ(1.5, PyFloat_AsDouble(subfloat));
307
308 PyObjectPtr subfloat_foo(testing::mainModuleGet("subfloat_foo"));
309 ASSERT_TRUE(PyLong_Check(subfloat_foo));
310 EXPECT_EQ(3, PyLong_AsLong(subfloat_foo));
311}
312
313TEST_F(FloatExtensionApiTest, GetMaxReturnsDblMax) {
314 EXPECT_EQ(PyFloat_GetMax(), DBL_MAX);
315}
316
317TEST_F(FloatExtensionApiTest, GetMinReturnsDblMin) {
318 EXPECT_EQ(PyFloat_GetMin(), DBL_MIN);
319}
320
321TEST_F(FloatExtensionApiTest, Pack2) {
322 double expected = 1.5;
323 unsigned char ptr[2] = {};
324 ASSERT_EQ(_PyFloat_Pack2(expected, ptr, /* le */ true), 0);
325 // 00000000 00111110
326 ASSERT_EQ(ptr[0], 0);
327 ASSERT_EQ(ptr[1], 62);
328 EXPECT_EQ(_PyFloat_Unpack2(ptr, /* le */ true), 1.5);
329}
330
331TEST_F(FloatExtensionApiTest, Pack4) {
332 double expected = 1.5;
333 unsigned char ptr[4] = {};
334 ASSERT_EQ(_PyFloat_Pack4(expected, ptr, /* le */ true), 0);
335 // 0000000 0000000 11000000 00111111
336 ASSERT_EQ(ptr[0], 0);
337 ASSERT_EQ(ptr[1], 0);
338 ASSERT_EQ(ptr[2], 192);
339 ASSERT_EQ(ptr[3], 63);
340 EXPECT_EQ(_PyFloat_Unpack4(ptr, /* le */ true), 1.5);
341}
342
343TEST_F(FloatExtensionApiTest, Pack8) {
344 double expected = 1.5;
345 unsigned char ptr[8] = {};
346 ASSERT_EQ(_PyFloat_Pack8(expected, ptr, /* le */ true), 0);
347 // 0000000 0000000 0000000 0000000 0000000 0000000 11111000 00111111
348 ASSERT_EQ(ptr[0], 0);
349 ASSERT_EQ(ptr[1], 0);
350 ASSERT_EQ(ptr[2], 0);
351 ASSERT_EQ(ptr[3], 0);
352 ASSERT_EQ(ptr[4], 0);
353 ASSERT_EQ(ptr[5], 0);
354 ASSERT_EQ(ptr[6], 248);
355 ASSERT_EQ(ptr[7], 63);
356 EXPECT_EQ(_PyFloat_Unpack8(ptr, /* le */ true), 1.5);
357}
358
359TEST_F(FloatExtensionApiTest, PyReturnNanReturnsNan) {
360 PyObjectPtr module(PyModule_New("mod"));
361 binaryfunc meth = [](PyObject*, PyObject*) { Py_RETURN_NAN; };
362 static PyMethodDef foo_func = {"foo", meth, METH_NOARGS};
363 PyObjectPtr func(PyCFunction_NewEx(&foo_func, nullptr, module));
364 PyObjectPtr result(_PyObject_CallNoArg(func));
365 EXPECT_TRUE(std::isnan(PyFloat_AsDouble(result)));
366}
367
368TEST_F(FloatExtensionApiTest, PyReturnINFReturnsInf) {
369 PyObjectPtr module(PyModule_New("mod"));
370 binaryfunc meth = [](PyObject*, PyObject*) { Py_RETURN_INF(0); };
371 static PyMethodDef foo_func = {"foo", meth, METH_NOARGS};
372 PyObjectPtr func(PyCFunction_NewEx(&foo_func, nullptr, module));
373 PyObjectPtr result(_PyObject_CallNoArg(func));
374 EXPECT_TRUE(std::isinf(PyFloat_AsDouble(result)));
375 EXPECT_GT(PyFloat_AsDouble(result), 0.);
376}
377
378TEST_F(FloatExtensionApiTest, PyReturnINFReturnsNegativeInf) {
379 PyObjectPtr module(PyModule_New("mod"));
380 binaryfunc meth = [](PyObject*, PyObject*) { Py_RETURN_INF(-1); };
381 static PyMethodDef foo_func = {"foo", meth, METH_NOARGS};
382 PyObjectPtr func(PyCFunction_NewEx(&foo_func, nullptr, module));
383 PyObjectPtr result(_PyObject_CallNoArg(func));
384 EXPECT_TRUE(std::isinf(PyFloat_AsDouble(result)));
385 EXPECT_LT(PyFloat_AsDouble(result), 0.);
386}
387
388} // namespace testing
389} // namespace py