#include "ut3/py_test_engine.h" #include #define PY_CHECKER(Name, PyType, AsType, Type) \ struct TPy##Name##Checker { \ void operator()(PyObject* pyVal, Type expected) { \ UNIT_ASSERT(Py##PyType##_Check(pyVal)); \ Type val = Py##PyType##_As##AsType(pyVal); \ UNIT_ASSERT(val != static_cast(-1) || !PyErr_Occurred()); \ UNIT_ASSERT_EQUAL(val, expected); \ } \ }; #if PY_MAJOR_VERSION >= 3 PY_CHECKER(Long, Long, Long, long) #else PY_CHECKER(Int, Int, Long, long) #endif #ifdef HAVE_LONG_LONG PY_CHECKER(LLong, Long, LongLong, long long) PY_CHECKER(Ulong, Long, UnsignedLongLong, unsigned long long) #else PY_CHECKER(LLong, Long, Long, long) PY_CHECKER(Ulong, Long, UnsignedLong, unsigned long) #endif PY_CHECKER(Float, Float, Double, long) #undef PY_CHECKER using namespace NPython; Y_UNIT_TEST_SUITE(TPyNumberTest) { template void TestCastsInRange(T begin, T end) { for (T i = begin; i < end; i++) { TPyObjectPtr pyVal = PyCast(i); UNIT_ASSERT(pyVal.Get() != nullptr); TPyChecker c; c(pyVal.Get(), i); T cppVal = PyCast(pyVal.Get()); UNIT_ASSERT_EQUAL(cppVal, i); } } template void TestSignedCasts() { TPythonTestEngine engine; TestCastsInRange(Min(), Min() + range); TestCastsInRange(-range, range); TestCastsInRange(Max() - range, Max()); } template void TestUnsignedCasts() { TPythonTestEngine engine; TestCastsInRange(Min(), Min() + range); TestCastsInRange(Max() - range, Max()); } Y_UNIT_TEST(Bool) { TPythonTestEngine engine; UNIT_ASSERT_EQUAL(PyCast(Py_True), true); UNIT_ASSERT_EQUAL(PyCast(Py_False), false); TPyObjectPtr list = PyList_New(0); UNIT_ASSERT_EQUAL(PyCast(list.Get()), false); bool res1; UNIT_ASSERT(TryPyCast(list.Get(), res1)); UNIT_ASSERT_EQUAL(res1, false); PyList_Append(list.Get(), Py_None); UNIT_ASSERT_EQUAL(PyCast(list.Get()), true); bool res2; UNIT_ASSERT(TryPyCast(list.Get(), res2)); UNIT_ASSERT_EQUAL(res2, true); } Y_UNIT_TEST(Float) { TestSignedCasts(); } Y_UNIT_TEST(Double) { TestUnsignedCasts(); } Y_UNIT_TEST(I64) { TestSignedCasts(); } Y_UNIT_TEST(Ui64) { TestUnsignedCasts(); } #if PY_MAJOR_VERSION >= 3 Y_UNIT_TEST(I8) { TestSignedCasts(); } Y_UNIT_TEST(Ui8) { TestUnsignedCasts(); } Y_UNIT_TEST(I16) { TestSignedCasts(); } Y_UNIT_TEST(Ui16) { TestUnsignedCasts(); } Y_UNIT_TEST(I32) { TestSignedCasts(); } Y_UNIT_TEST(Ui32) { TestUnsignedCasts(); } Y_UNIT_TEST(ImplicitIntCasts) { TPythonTestEngine engine; const ui64 longMask = sizeof(long) == 4 ? Max() : Max(); i64 expected = longMask & (static_cast(Max()) + 10); TPyObjectPtr pyInt = PyLong_FromLong(expected); { // signed i64 actual = PyCast(pyInt.Get()); UNIT_ASSERT_EQUAL(actual, expected); bool isOk = TryPyCast(pyInt.Get(), actual); UNIT_ASSERT(isOk); UNIT_ASSERT_EQUAL(actual, expected); } { // unsigned ui64 actual = PyCast(pyInt.Get()); UNIT_ASSERT_EQUAL(actual, static_cast(expected)); bool isOk = TryPyCast(pyInt.Get(), actual); UNIT_ASSERT(isOk); UNIT_ASSERT_EQUAL(actual, static_cast(expected)); } { // to float float f = PyCast(pyInt.Get()); UNIT_ASSERT_DOUBLES_EQUAL(f, expected, 0.000001); bool isOk = TryPyCast(pyInt.Get(), f); UNIT_ASSERT(isOk); UNIT_ASSERT_DOUBLES_EQUAL(f, expected, 0.000001); } { // to double double d = PyCast(pyInt.Get()); UNIT_ASSERT_DOUBLES_EQUAL(d, expected, 0.000001); bool isOk = TryPyCast(pyInt.Get(), d); UNIT_ASSERT(isOk); UNIT_ASSERT_DOUBLES_EQUAL(d, expected, 0.000001); } // expected overflow i32 tmp; UNIT_ASSERT(!TryPyCast(pyInt.Get(), tmp)); ui32 tmpu; UNIT_ASSERT(!TryPyCast(pyInt.Get(), tmpu)); } #else Y_UNIT_TEST(I8) { TestSignedCasts(); } Y_UNIT_TEST(Ui8) { TestUnsignedCasts(); } Y_UNIT_TEST(I16) { TestSignedCasts(); } Y_UNIT_TEST(Ui16) { TestUnsignedCasts(); } Y_UNIT_TEST(I32) { TestSignedCasts(); } Y_UNIT_TEST(Ui32) { if (sizeof(long) == 4) { TestUnsignedCasts(); } else { TestUnsignedCasts(); } } Y_UNIT_TEST(ImplicitIntCasts) { TPythonTestEngine engine; const ui64 longMask = sizeof(long) == 4 ? Max() : Max(); i64 expected = longMask & (static_cast(Max()) + 10); TPyObjectPtr pyInt = PyInt_FromLong(expected); { // signed i64 actual = PyCast(pyInt.Get()); UNIT_ASSERT_EQUAL(actual, expected); bool isOk = TryPyCast(pyInt.Get(), actual); UNIT_ASSERT(isOk); UNIT_ASSERT_EQUAL(actual, expected); } { // unsigned ui64 actual = PyCast(pyInt.Get()); UNIT_ASSERT_EQUAL(actual, static_cast(expected)); bool isOk = TryPyCast(pyInt.Get(), actual); UNIT_ASSERT(isOk); UNIT_ASSERT_EQUAL(actual, static_cast(expected)); } { // to float float f = PyCast(pyInt.Get()); UNIT_ASSERT_DOUBLES_EQUAL(f, expected, 0.000001); bool isOk = TryPyCast(pyInt.Get(), f); UNIT_ASSERT(isOk); UNIT_ASSERT_DOUBLES_EQUAL(f, expected, 0.000001); } { // to double double d = PyCast(pyInt.Get()); UNIT_ASSERT_DOUBLES_EQUAL(d, expected, 0.000001); bool isOk = TryPyCast(pyInt.Get(), d); UNIT_ASSERT(isOk); UNIT_ASSERT_DOUBLES_EQUAL(d, expected, 0.000001); } // expected overflow i32 tmp; UNIT_ASSERT(!TryPyCast(pyInt.Get(), tmp)); ui32 tmpu; UNIT_ASSERT(!TryPyCast(pyInt.Get(), tmpu)); } #endif Y_UNIT_TEST(ImplicitLongCasts) { TPythonTestEngine engine; i64 expected = static_cast(Max()) + 10; TPyObjectPtr pyLong; #ifdef HAVE_LONG_LONG pyLong = PyLong_FromLongLong(expected); #else pyLong = PyLong_FromLong(expected) #endif { // signed i64 actual = PyCast(pyLong.Get()); UNIT_ASSERT_EQUAL(actual, expected); bool isOk = TryPyCast(pyLong.Get(), actual); UNIT_ASSERT(isOk); UNIT_ASSERT_EQUAL(actual, expected); } { // unsigned ui64 actual = PyCast(pyLong.Get()); UNIT_ASSERT_EQUAL(actual, static_cast(expected)); bool isOk = TryPyCast(pyLong.Get(), actual); UNIT_ASSERT(isOk); UNIT_ASSERT_EQUAL(actual, static_cast(expected)); } { // to float float f = PyCast(pyLong.Get()); UNIT_ASSERT_DOUBLES_EQUAL(f, expected, 0.000001); bool isOk = TryPyCast(pyLong.Get(), f); UNIT_ASSERT(isOk); UNIT_ASSERT_DOUBLES_EQUAL(f, expected, 0.000001); } { // to double double d = PyCast(pyLong.Get()); UNIT_ASSERT_DOUBLES_EQUAL(d, expected, 0.000001); bool isOk = TryPyCast(pyLong.Get(), d); UNIT_ASSERT(isOk); UNIT_ASSERT_DOUBLES_EQUAL(d, expected, 0.000001); } // expected overflow i8 tmp; UNIT_ASSERT(!TryPyCast(pyLong.Get(), tmp)); } Y_UNIT_TEST(HugeLongOverflow) { TPythonTestEngine engine; TPyObjectPtr pyLong = PyLong_FromString((char*)"0xfffffffffffffffff", nullptr, 0); TPyObjectPtr bitLength = PyObject_CallMethod(pyLong.Get(), (char*)"bit_length", (char*)"()"); UNIT_ASSERT_EQUAL(PyCast(bitLength.Get()), 68); // 68 bits number ui64 resUI64; UNIT_ASSERT(!TryPyCast(pyLong.Get(), resUI64)); i64 resI64; UNIT_ASSERT(!TryPyCast(pyLong.Get(), resI64)); ui32 resUI32; UNIT_ASSERT(!TryPyCast(pyLong.Get(), resUI32)); i32 resI32; UNIT_ASSERT(!TryPyCast(pyLong.Get(), resI32)); ui16 resUI16; UNIT_ASSERT(!TryPyCast(pyLong.Get(), resUI16)); i16 resI16; UNIT_ASSERT(!TryPyCast(pyLong.Get(), resI16)); ui8 resUI8; UNIT_ASSERT(!TryPyCast(pyLong.Get(), resUI8)); i8 resI8; UNIT_ASSERT(!TryPyCast(pyLong.Get(), resI8)); } Y_UNIT_TEST(ImplicitFloatCasts) { TPythonTestEngine engine; double expected = 3.14159; TPyObjectPtr pyFloat = PyFloat_FromDouble(expected); { // to float float f = PyCast(pyFloat.Get()); UNIT_ASSERT_DOUBLES_EQUAL(f, expected, 0.000001); bool isOk = TryPyCast(pyFloat.Get(), f); UNIT_ASSERT(isOk); UNIT_ASSERT_DOUBLES_EQUAL(f, expected, 0.000001); } { // to double double d = PyCast(pyFloat.Get()); UNIT_ASSERT_DOUBLES_EQUAL(d, expected, 0.000001); bool isOk = TryPyCast(pyFloat.Get(), d); UNIT_ASSERT(isOk); UNIT_ASSERT_DOUBLES_EQUAL(d, expected, 0.000001); } } }