summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
Diffstat (limited to 'Modules')
-rw-r--r--Modules/_testcapimodule.c229
-rw-r--r--Modules/testcapi_long.h167
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;
+}