diff options
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_testcapimodule.c | 229 | ||||
-rw-r--r-- | Modules/testcapi_long.h | 167 |
2 files changed, 238 insertions, 158 deletions
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 06602e9..6e2208e 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -172,9 +172,58 @@ test_dict_iteration(PyObject* self, PyObject* args) return Py_None; } -#ifdef HAVE_LONG_LONG -/* Basic sanity checks for PyLong_{As, From}{Unsigned,}LongLong(). */ +/* Tests of PyLong_{As, From}{Unsigned,}Long(), and (#ifdef HAVE_LONG_LONG) + PyLong_{As, From}{Unsigned,}LongLong()/ + + Note that the meat of the test is contained in testcapi_long.h. + This is revolting, but delicate code duplication is worse: "almost + exactly the same" code is needed to test LONG_LONG, but the ubiquitous + dependence on type names makes it impossible to use a parameterized + function. A giant macro would be even worse than this. A C++ template + would be perfect. + + The "report an error" functions are deliberately not part of the #include + file: if the test fails, you can set a breakpoint in the appropriate + error function directly, and crawl back from there in the debugger. +*/ + +#define UNBIND(X) Py_DECREF(X); (X) = NULL + +static PyObject * +raise_test_long_error(const char* msg) +{ + return raiseTestError("test_long_api", msg); +} + +#define TESTNAME test_long_api_inner +#define TYPENAME long +#define F_S_TO_PY PyLong_FromLong +#define F_PY_TO_S PyLong_AsLong +#define F_U_TO_PY PyLong_FromUnsignedLong +#define F_PY_TO_U PyLong_AsUnsignedLong +#define F_ERROR raise_test_long_error + +#include "testcapi_long.h" + +static PyObject * +test_long_api(PyObject* self, PyObject* args) +{ + if (!PyArg_ParseTuple(args, ":test_long_api")) + return NULL; + + return TESTNAME(); +} + +#undef TESTNAME +#undef TYPENAME +#undef F_S_TO_PY +#undef F_PY_TO_S +#undef F_U_TO_PY +#undef F_PY_TO_U +#undef F_ERROR + +#ifdef HAVE_LONG_LONG static PyObject * raise_test_longlong_error(const char* msg) @@ -182,177 +231,41 @@ raise_test_longlong_error(const char* msg) return raiseTestError("test_longlong_api", msg); } -#define UNBIND(X) Py_DECREF(X); (X) = NULL +#define TESTNAME test_longlong_api_inner +#define TYPENAME LONG_LONG +#define F_S_TO_PY PyLong_FromLongLong +#define F_PY_TO_S PyLong_AsLongLong +#define F_U_TO_PY PyLong_FromUnsignedLongLong +#define F_PY_TO_U PyLong_AsUnsignedLongLong +#define F_ERROR raise_test_longlong_error + +#include "testcapi_long.h" static PyObject * test_longlong_api(PyObject* self, PyObject* args) { - const int NBITS = SIZEOF_LONG_LONG * 8; - unsigned LONG_LONG base; - PyObject *pyresult; - int i; - if (!PyArg_ParseTuple(args, ":test_longlong_api")) return NULL; + return TESTNAME(); +} - /* Note: This test lets PyObjects leak if an error is raised. Since - an error should never be raised, leaks are impossible <wink>. */ - - /* Test native -> PyLong -> native roundtrip identity. - * Generate all powers of 2, and test them and their negations, - * plus the numbers +-1 off from them. - */ - base = 1; - for (i = 0; - i < NBITS + 1; /* on last, base overflows to 0 */ - ++i, base <<= 1) - { - int j; - for (j = 0; j < 6; ++j) { - LONG_LONG in, out; - unsigned LONG_LONG uin, uout; - - /* For 0, 1, 2 use base; for 3, 4, 5 use -base */ - uin = j < 3 ? base - : (unsigned LONG_LONG)(-(LONG_LONG)base); - - /* For 0 & 3, subtract 1. - * For 1 & 4, leave alone. - * For 2 & 5, add 1. - */ - uin += (unsigned LONG_LONG)(LONG_LONG)(j % 3 - 1); - - pyresult = PyLong_FromUnsignedLongLong(uin); - if (pyresult == NULL) - return raise_test_longlong_error( - "unsigned unexpected null result"); - - uout = PyLong_AsUnsignedLongLong(pyresult); - if (uout == (unsigned LONG_LONG)-1 && PyErr_Occurred()) - return raise_test_longlong_error( - "unsigned unexpected -1 result"); - if (uout != uin) - return raise_test_longlong_error( - "unsigned output != input"); - UNBIND(pyresult); - - in = (LONG_LONG)uin; - pyresult = PyLong_FromLongLong(in); - if (pyresult == NULL) - return raise_test_longlong_error( - "signed unexpected null result"); - - out = PyLong_AsLongLong(pyresult); - if (out == (LONG_LONG)-1 && PyErr_Occurred()) - return raise_test_longlong_error( - "signed unexpected -1 result"); - if (out != in) - return raise_test_longlong_error( - "signed output != input"); - UNBIND(pyresult); - } - } - - /* Overflow tests. The loop above ensured that all limit cases that - * should not overflow don't overflow, so all we need to do here is - * provoke one-over-the-limit cases (not exhaustive, but sharp). - */ - { - PyObject *one, *x, *y; - LONG_LONG out; - unsigned LONG_LONG uout; - - one = PyLong_FromLong(1); - if (one == NULL) - return raise_test_longlong_error( - "unexpected NULL from PyLong_FromLong"); - - /* Unsigned complains about -1? */ - x = PyNumber_Negative(one); - if (x == NULL) - return raise_test_longlong_error( - "unexpected NULL from PyNumber_Negative"); - - uout = PyLong_AsUnsignedLongLong(x); - if (uout != (unsigned LONG_LONG)-1 || !PyErr_Occurred()) - return raise_test_longlong_error( - "PyLong_AsUnsignedLongLong(-1) didn't " - "complain"); - PyErr_Clear(); - UNBIND(x); - - /* Unsigned complains about 2**NBITS? */ - y = PyLong_FromLong((long)NBITS); - if (y == NULL) - return raise_test_longlong_error( - "unexpected NULL from PyLong_FromLong"); - - x = PyNumber_Lshift(one, y); /* 1L << NBITS, == 2**NBITS */ - UNBIND(y); - if (x == NULL) - return raise_test_longlong_error( - "unexpected NULL from PyNumber_Lshift"); - - uout = PyLong_AsUnsignedLongLong(x); - if (uout != (unsigned LONG_LONG)-1 || !PyErr_Occurred()) - return raise_test_longlong_error( - "PyLong_AsUnsignedLongLong(2**NBITS) didn't " - "complain"); - PyErr_Clear(); - - /* Signed complains about 2**(NBITS-1)? - x still has 2**NBITS. */ - y = PyNumber_Rshift(x, one); /* 2**(NBITS-1) */ - UNBIND(x); - if (y == NULL) - return raise_test_longlong_error( - "unexpected NULL from PyNumber_Rshift"); - - out = PyLong_AsLongLong(y); - if (out != (LONG_LONG)-1 || !PyErr_Occurred()) - return raise_test_longlong_error( - "PyLong_AsLongLong(2**(NBITS-1)) didn't " - "complain"); - PyErr_Clear(); - - /* Signed complains about -2**(NBITS-1)-1?; - y still has 2**(NBITS-1). */ - x = PyNumber_Negative(y); /* -(2**(NBITS-1)) */ - UNBIND(y); - if (x == NULL) - return raise_test_longlong_error( - "unexpected NULL from PyNumber_Negative"); - - y = PyNumber_Subtract(x, one); /* -(2**(NBITS-1))-1 */ - UNBIND(x); - if (y == NULL) - return raise_test_longlong_error( - "unexpected NULL from PyNumber_Subtract"); - - out = PyLong_AsLongLong(y); - if (out != (LONG_LONG)-1 || !PyErr_Occurred()) - return raise_test_longlong_error( - "PyLong_AsLongLong(-2**(NBITS-1)-1) didn't " - "complain"); - PyErr_Clear(); - UNBIND(y); - - Py_XDECREF(x); - Py_XDECREF(y); - Py_DECREF(one); - } +#undef TESTNAME +#undef TYPENAME +#undef F_S_TO_PY +#undef F_PY_TO_S +#undef F_U_TO_PY +#undef F_PY_TO_U +#undef F_ERROR - Py_INCREF(Py_None); - return Py_None; -} +#endif /* ifdef HAVE_LONG_LONG */ -#endif static PyMethodDef TestMethods[] = { {"test_config", test_config, METH_VARARGS}, {"test_list_api", test_list_api, METH_VARARGS}, {"test_dict_iteration", test_dict_iteration, METH_VARARGS}, + {"test_long_api", test_long_api, METH_VARARGS}, #ifdef HAVE_LONG_LONG {"test_longlong_api", test_longlong_api, METH_VARARGS}, #endif diff --git a/Modules/testcapi_long.h b/Modules/testcapi_long.h new file mode 100644 index 0000000..7c5a7e7 --- /dev/null +++ b/Modules/testcapi_long.h @@ -0,0 +1,167 @@ +/* Poor-man's template. Macros used: + TESTNAME name of the test (like test_long_api_inner) + TYPENAME the signed type (like long) + F_S_TO_PY convert signed to pylong; TYPENAME -> PyObject* + F_PY_TO_S convert pylong to signed; PyObject* -> TYPENAME + F_U_TO_PY convert unsigned to pylong; unsigned TYPENAME -> PyObject* + F_PY_TO_U convert pylong to unsigned; PyObject* -> TypeError + F_ERROR error-report function; char* -> PyObject* (returns NULL) +*/ + +static PyObject * +TESTNAME() +{ + const int NBITS = sizeof(TYPENAME) * 8; + unsigned TYPENAME base; + PyObject *pyresult; + int i; + + /* Note: This test lets PyObjects leak if an error is raised. Since + an error should never be raised, leaks are impossible <wink>. */ + + /* Test native -> PyLong -> native roundtrip identity. + * Generate all powers of 2, and test them and their negations, + * plus the numbers +-1 off from them. + */ + base = 1; + for (i = 0; + i < NBITS + 1; /* on last, base overflows to 0 */ + ++i, base <<= 1) + { + int j; + for (j = 0; j < 6; ++j) { + TYPENAME in, out; + unsigned TYPENAME uin, uout; + + /* For 0, 1, 2 use base; for 3, 4, 5 use -base */ + uin = j < 3 ? base + : (unsigned TYPENAME)(-(TYPENAME)base); + + /* For 0 & 3, subtract 1. + * For 1 & 4, leave alone. + * For 2 & 5, add 1. + */ + uin += (unsigned TYPENAME)(TYPENAME)(j % 3 - 1); + + pyresult = F_U_TO_PY(uin); + if (pyresult == NULL) + return F_ERROR( + "unsigned unexpected null result"); + + uout = F_PY_TO_U(pyresult); + if (uout == (unsigned TYPENAME)-1 && PyErr_Occurred()) + return F_ERROR( + "unsigned unexpected -1 result"); + if (uout != uin) + return F_ERROR( + "unsigned output != input"); + UNBIND(pyresult); + + in = (TYPENAME)uin; + pyresult = F_S_TO_PY(in); + if (pyresult == NULL) + return F_ERROR( + "signed unexpected null result"); + + out = F_PY_TO_S(pyresult); + if (out == (TYPENAME)-1 && PyErr_Occurred()) + return F_ERROR( + "signed unexpected -1 result"); + if (out != in) + return F_ERROR( + "signed output != input"); + UNBIND(pyresult); + } + } + + /* Overflow tests. The loop above ensured that all limit cases that + * should not overflow don't overflow, so all we need to do here is + * provoke one-over-the-limit cases (not exhaustive, but sharp). + */ + { + PyObject *one, *x, *y; + TYPENAME out; + unsigned TYPENAME uout; + + one = PyLong_FromLong(1); + if (one == NULL) + return F_ERROR( + "unexpected NULL from PyLong_FromLong"); + + /* Unsigned complains about -1? */ + x = PyNumber_Negative(one); + if (x == NULL) + return F_ERROR( + "unexpected NULL from PyNumber_Negative"); + + uout = F_PY_TO_U(x); + if (uout != (unsigned TYPENAME)-1 || !PyErr_Occurred()) + return F_ERROR( + "PyLong_AsUnsignedXXX(-1) didn't complain"); + PyErr_Clear(); + UNBIND(x); + + /* Unsigned complains about 2**NBITS? */ + y = PyLong_FromLong((long)NBITS); + if (y == NULL) + return F_ERROR( + "unexpected NULL from PyLong_FromLong"); + + x = PyNumber_Lshift(one, y); /* 1L << NBITS, == 2**NBITS */ + UNBIND(y); + if (x == NULL) + return F_ERROR( + "unexpected NULL from PyNumber_Lshift"); + + uout = F_PY_TO_U(x); + if (uout != (unsigned TYPENAME)-1 || !PyErr_Occurred()) + return F_ERROR( + "PyLong_AsUnsignedXXX(2**NBITS) didn't " + "complain"); + PyErr_Clear(); + + /* Signed complains about 2**(NBITS-1)? + x still has 2**NBITS. */ + y = PyNumber_Rshift(x, one); /* 2**(NBITS-1) */ + UNBIND(x); + if (y == NULL) + return F_ERROR( + "unexpected NULL from PyNumber_Rshift"); + + out = F_PY_TO_S(y); + if (out != (TYPENAME)-1 || !PyErr_Occurred()) + return F_ERROR( + "PyLong_AsXXX(2**(NBITS-1)) didn't " + "complain"); + PyErr_Clear(); + + /* Signed complains about -2**(NBITS-1)-1?; + y still has 2**(NBITS-1). */ + x = PyNumber_Negative(y); /* -(2**(NBITS-1)) */ + UNBIND(y); + if (x == NULL) + return F_ERROR( + "unexpected NULL from PyNumber_Negative"); + + y = PyNumber_Subtract(x, one); /* -(2**(NBITS-1))-1 */ + UNBIND(x); + if (y == NULL) + return F_ERROR( + "unexpected NULL from PyNumber_Subtract"); + + out = F_PY_TO_S(y); + if (out != (TYPENAME)-1 || !PyErr_Occurred()) + return F_ERROR( + "PyLong_AsXXX(-2**(NBITS-1)-1) didn't " + "complain"); + PyErr_Clear(); + UNBIND(y); + + Py_XDECREF(x); + Py_XDECREF(y); + Py_DECREF(one); + } + + Py_INCREF(Py_None); + return Py_None; +} |