1/* Poor-man's template.  Macros used:
2   TESTNAME     name of the test (like test_long_api_inner)
3   TYPENAME     the signed type (like long)
4   F_S_TO_PY    convert signed to pylong; TYPENAME -> PyObject*
5   F_PY_TO_S    convert pylong to signed; PyObject* -> TYPENAME
6   F_U_TO_PY    convert unsigned to pylong; unsigned TYPENAME -> PyObject*
7   F_PY_TO_U    convert pylong to unsigned; PyObject* -> unsigned TYPENAME
8*/
9
10static PyObject *
11TESTNAME(PyObject *error(const char*))
12{
13    const int NBITS = sizeof(TYPENAME) * 8;
14    unsigned TYPENAME base;
15    PyObject *pyresult;
16    int i;
17
18    /* Note:  This test lets PyObjects leak if an error is raised.  Since
19       an error should never be raised, leaks are impossible <wink>. */
20
21    /* Test native -> PyLong -> native roundtrip identity.
22     * Generate all powers of 2, and test them and their negations,
23     * plus the numbers +-1 off from them.
24     */
25    base = 1;
26    for (i = 0;
27         i < NBITS + 1;  /* on last, base overflows to 0 */
28         ++i, base <<= 1)
29    {
30        int j;
31        for (j = 0; j < 6; ++j) {
32            TYPENAME in, out;
33            unsigned TYPENAME uin, uout;
34
35            /* For 0, 1, 2 use base; for 3, 4, 5 use -base */
36            uin = j < 3 ? base : 0U - base;
37
38            /* For 0 & 3, subtract 1.
39             * For 1 & 4, leave alone.
40             * For 2 & 5, add 1.
41             */
42            uin += (unsigned TYPENAME)(TYPENAME)(j % 3 - 1);
43
44            pyresult = F_U_TO_PY(uin);
45            if (pyresult == NULL)
46                return error(
47                 "unsigned unexpected null result");
48
49            uout = F_PY_TO_U(pyresult);
50            if (uout == (unsigned TYPENAME)-1 && PyErr_Occurred())
51                return error(
52                    "unsigned unexpected -1 result");
53            if (uout != uin)
54                return error(
55                    "unsigned output != input");
56            UNBIND(pyresult);
57
58            in = (TYPENAME)uin;
59            pyresult = F_S_TO_PY(in);
60            if (pyresult == NULL)
61                return error(
62                    "signed unexpected null result");
63
64            out = F_PY_TO_S(pyresult);
65            if (out == (TYPENAME)-1 && PyErr_Occurred())
66                return error(
67                    "signed unexpected -1 result");
68            if (out != in)
69                return error(
70                    "signed output != input");
71            UNBIND(pyresult);
72        }
73    }
74
75    /* Overflow tests.  The loop above ensured that all limit cases that
76     * should not overflow don't overflow, so all we need to do here is
77     * provoke one-over-the-limit cases (not exhaustive, but sharp).
78     */
79    {
80        PyObject *one, *x, *y;
81        TYPENAME out;
82        unsigned TYPENAME uout;
83
84        one = PyLong_FromLong(1);
85        if (one == NULL)
86            return error(
87                "unexpected NULL from PyLong_FromLong");
88
89        /* Unsigned complains about -1? */
90        x = PyNumber_Negative(one);
91        if (x == NULL)
92            return error(
93                "unexpected NULL from PyNumber_Negative");
94
95        uout = F_PY_TO_U(x);
96        if (uout != (unsigned TYPENAME)-1 || !PyErr_Occurred())
97            return error(
98                "PyLong_AsUnsignedXXX(-1) didn't complain");
99        if (!PyErr_ExceptionMatches(PyExc_OverflowError))
100            return error(
101                "PyLong_AsUnsignedXXX(-1) raised "
102                "something other than OverflowError");
103        PyErr_Clear();
104        UNBIND(x);
105
106        /* Unsigned complains about 2**NBITS? */
107        y = PyLong_FromLong((long)NBITS);
108        if (y == NULL)
109            return error(
110                "unexpected NULL from PyLong_FromLong");
111
112        x = PyNumber_Lshift(one, y); /* 1L << NBITS, == 2**NBITS */
113        UNBIND(y);
114        if (x == NULL)
115            return error(
116                "unexpected NULL from PyNumber_Lshift");
117
118        uout = F_PY_TO_U(x);
119        if (uout != (unsigned TYPENAME)-1 || !PyErr_Occurred())
120            return error(
121                "PyLong_AsUnsignedXXX(2**NBITS) didn't "
122                "complain");
123        if (!PyErr_ExceptionMatches(PyExc_OverflowError))
124            return error(
125                "PyLong_AsUnsignedXXX(2**NBITS) raised "
126                "something other than OverflowError");
127        PyErr_Clear();
128
129        /* Signed complains about 2**(NBITS-1)?
130           x still has 2**NBITS. */
131        y = PyNumber_Rshift(x, one); /* 2**(NBITS-1) */
132        UNBIND(x);
133        if (y == NULL)
134            return error(
135                "unexpected NULL from PyNumber_Rshift");
136
137        out = F_PY_TO_S(y);
138        if (out != (TYPENAME)-1 || !PyErr_Occurred())
139            return error(
140                "PyLong_AsXXX(2**(NBITS-1)) didn't "
141                "complain");
142        if (!PyErr_ExceptionMatches(PyExc_OverflowError))
143            return error(
144                "PyLong_AsXXX(2**(NBITS-1)) raised "
145                "something other than OverflowError");
146        PyErr_Clear();
147
148        /* Signed complains about -2**(NBITS-1)-1?;
149           y still has 2**(NBITS-1). */
150        x = PyNumber_Negative(y);  /* -(2**(NBITS-1)) */
151        UNBIND(y);
152        if (x == NULL)
153            return error(
154                "unexpected NULL from PyNumber_Negative");
155
156        y = PyNumber_Subtract(x, one); /* -(2**(NBITS-1))-1 */
157        UNBIND(x);
158        if (y == NULL)
159            return error(
160                "unexpected NULL from PyNumber_Subtract");
161
162        out = F_PY_TO_S(y);
163        if (out != (TYPENAME)-1 || !PyErr_Occurred())
164            return error(
165                "PyLong_AsXXX(-2**(NBITS-1)-1) didn't "
166                "complain");
167        if (!PyErr_ExceptionMatches(PyExc_OverflowError))
168            return error(
169                "PyLong_AsXXX(-2**(NBITS-1)-1) raised "
170                "something other than OverflowError");
171        PyErr_Clear();
172        UNBIND(y);
173
174        Py_XDECREF(x);
175        Py_XDECREF(y);
176        Py_DECREF(one);
177    }
178
179    /* Test F_PY_TO_{S,U} on non-pylong input. This should raise a TypeError. */
180    {
181        TYPENAME out;
182        unsigned TYPENAME uout;
183
184        Py_INCREF(Py_None);
185
186        out = F_PY_TO_S(Py_None);
187        if (out != (TYPENAME)-1 || !PyErr_Occurred())
188            return error("PyLong_AsXXX(None) didn't complain");
189        if (!PyErr_ExceptionMatches(PyExc_TypeError))
190            return error("PyLong_AsXXX(None) raised "
191                         "something other than TypeError");
192        PyErr_Clear();
193
194        uout = F_PY_TO_U(Py_None);
195        if (uout != (unsigned TYPENAME)-1 || !PyErr_Occurred())
196            return error("PyLong_AsXXX(None) didn't complain");
197        if (!PyErr_ExceptionMatches(PyExc_TypeError))
198            return error("PyLong_AsXXX(None) raised "
199                         "something other than TypeError");
200        PyErr_Clear();
201
202        Py_DECREF(Py_None);
203    }
204
205    Py_INCREF(Py_None);
206    return Py_None;
207}
208