runtime/float-builtins-test.cpp (895 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include "float-builtins.h"
#include <limits>
#include "gtest/gtest.h"
#include "builtins.h"
#include "handles.h"
#include "int-builtins.h"
#include "objects.h"
#include "runtime.h"
#include "test-utils.h"
namespace py {
namespace testing {
using FloatBuiltinsTest = testing::RuntimeFixture;
TEST(FloatBuiltinsTestNoFixture,
DecodeDoubleWithPositiveDoubleReturnsIsNegFalse) {
double input = 100.0;
bool is_neg;
int exp;
int64_t mantissa;
decodeDouble(input, &is_neg, &exp, &mantissa);
EXPECT_EQ(is_neg, false);
}
TEST(FloatBuiltinsTestNoFixture,
DecodeDoubleWithNegativeDoubleReturnsIsNegTrue) {
double input = -100.0;
bool is_neg;
int exp;
int64_t mantissa;
decodeDouble(input, &is_neg, &exp, &mantissa);
EXPECT_EQ(is_neg, true);
}
TEST(FloatBuiltinsTestNoFixture,
DecodeDoubleWithMaximumExponentReturnsCorrectValue) {
double input = std::strtod("0x1.0p+1024", nullptr);
bool is_neg;
int exp;
int64_t mantissa;
decodeDouble(input, &is_neg, &exp, &mantissa);
EXPECT_EQ(exp, 1024);
}
TEST(FloatBuiltinsTestNoFixture,
DecodeDoubleWithMinimumExponentReturnsCorrectValue) {
double input = std::strtod("0x1.0p-1023", nullptr);
bool is_neg;
int exp;
int64_t mantissa;
decodeDouble(input, &is_neg, &exp, &mantissa);
EXPECT_EQ(exp, -1023);
}
TEST(FloatBuiltinsTestNoFixture, DecodeDoubleWithMantissaReturnsCorrectValue) {
double input = std::strtod("0x1.29ef685b3f6fbp+52", nullptr);
bool is_neg;
int exp;
int64_t mantissa;
decodeDouble(input, &is_neg, &exp, &mantissa);
EXPECT_EQ(mantissa, 0x29ef685b3f6fb);
}
TEST_F(FloatBuiltinsTest, DunderMulWithDoubleReturnsDouble) {
HandleScope scope(thread_);
Float left(&scope, runtime_->newFloat(2.0));
Float right(&scope, runtime_->newFloat(1.5));
Object result(&scope, runBuiltin(METH(float, __mul__), left, right));
EXPECT_TRUE(isFloatEqualsDouble(*result, 3.0));
}
TEST_F(FloatBuiltinsTest, DunderMulWithSmallIntReturnsDouble) {
HandleScope scope(thread_);
Float left(&scope, runtime_->newFloat(2.5));
Int right(&scope, runtime_->newInt(1));
Object result(&scope, runBuiltin(METH(float, __mul__), left, right));
EXPECT_TRUE(isFloatEqualsDouble(*result, 2.5));
}
TEST_F(FloatBuiltinsTest, DunderMulWithNonFloatSelfRaisesTypeError) {
HandleScope scope(thread_);
Object left(&scope, NoneType::object());
Float right(&scope, runtime_->newFloat(1.0));
Object result(&scope, runBuiltin(METH(float, __mul__), left, right));
EXPECT_TRUE(raised(*result, LayoutId::kTypeError));
}
TEST_F(FloatBuiltinsTest, DunderMulWithNonFloatOtherReturnsNotImplemented) {
HandleScope scope(thread_);
Float left(&scope, runtime_->newFloat(1.0));
Object right(&scope, NoneType::object());
Object result(&scope, runBuiltin(METH(float, __mul__), left, right));
EXPECT_TRUE(result.isNotImplementedType());
}
TEST_F(FloatBuiltinsTest, DunderNeWithInequalFloatsReturnsTrue) {
ASSERT_FALSE(
runFromCStr(runtime_, "result = float.__ne__(12.2, 2.12)").isError());
EXPECT_EQ(mainModuleAt(runtime_, "result"), Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderNeWithEqualFloatIntReturnsFalse) {
ASSERT_FALSE(
runFromCStr(runtime_, "result = float.__ne__(34.0, 34)").isError());
EXPECT_EQ(mainModuleAt(runtime_, "result"), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderNeWithStringReturnsNotImplemented) {
ASSERT_FALSE(
runFromCStr(runtime_, "result = float.__ne__(5.5, '')").isError());
EXPECT_TRUE(mainModuleAt(runtime_, "result").isNotImplementedType());
}
TEST_F(FloatBuiltinsTest, DunderAbsZeroReturnsZero) {
HandleScope scope(thread_);
Float self(&scope, runtime_->newFloat(0.0));
Object result(&scope, runBuiltin(METH(float, __abs__), self));
EXPECT_TRUE(isFloatEqualsDouble(*result, 0.0));
}
TEST_F(FloatBuiltinsTest, DunderAbsNegativeReturnsPositive) {
HandleScope scope(thread_);
Float self(&scope, runtime_->newFloat(-1234.0));
Object result(&scope, runBuiltin(METH(float, __abs__), self));
EXPECT_TRUE(isFloatEqualsDouble(*result, 1234.0));
}
TEST_F(FloatBuiltinsTest, DunderAbsPositiveReturnsPositive) {
HandleScope scope(thread_);
Float self(&scope, runtime_->newFloat(5678.0));
Object result(&scope, runBuiltin(METH(float, __abs__), self));
EXPECT_TRUE(isFloatEqualsDouble(*result, 5678.0));
}
TEST_F(FloatBuiltinsTest, BinaryAddDouble) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
a = 2.0
b = 1.5
c = a + b
)")
.isError());
Object c(&scope, mainModuleAt(runtime_, "c"));
EXPECT_TRUE(isFloatEqualsDouble(*c, 3.5));
}
TEST_F(FloatBuiltinsTest, BinaryAddSmallInt) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
a = 2.5
b = 1
c = a + b
)")
.isError());
Object c(&scope, mainModuleAt(runtime_, "c"));
EXPECT_TRUE(isFloatEqualsDouble(*c, 3.5));
}
TEST_F(FloatBuiltinsTest, AddWithNonFloatOtherRaisesTypeError) {
const char* src = R"(
1.0 + None
)";
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, src), LayoutId::kTypeError,
"float.__add__(NoneType) is not supported"));
}
TEST_F(FloatBuiltinsTest, DunderAddWithFloatSubclassReturnsFloatSum) {
HandleScope scope(thread_);
EXPECT_FALSE(runFromCStr(runtime_, R"(
class SubFloat(float):
pass
left = SubFloat(1.0)
right = SubFloat(2.0)
)")
.isError());
Object left(&scope, mainModuleAt(runtime_, "left"));
Object right(&scope, mainModuleAt(runtime_, "right"));
Object result(&scope, runBuiltin(METH(float, __add__), left, right));
EXPECT_TRUE(isFloatEqualsDouble(*result, 3.0));
}
TEST_F(FloatBuiltinsTest, DunderBoolWithZeroReturnsFalse) {
HandleScope scope(thread_);
Float self(&scope, runtime_->newFloat(0.0));
Object result(&scope, runBuiltin(METH(float, __bool__), self));
EXPECT_EQ(*result, Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderBoolWithNonZeroReturnsTrue) {
HandleScope scope(thread_);
Float self(&scope, runtime_->newFloat(1234.0));
Object result(&scope, runBuiltin(METH(float, __bool__), self));
EXPECT_EQ(*result, Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderTrueDivWithDoubleReturnsDouble) {
HandleScope scope(thread_);
Float left(&scope, runtime_->newFloat(3.0));
Float right(&scope, runtime_->newFloat(2.0));
Object result(&scope, runBuiltin(METH(float, __truediv__), left, right));
EXPECT_TRUE(isFloatEqualsDouble(*result, 1.5));
}
TEST_F(FloatBuiltinsTest, DunderTrueDivWithSmallIntReturnsDouble) {
HandleScope scope(thread_);
Float left(&scope, runtime_->newFloat(3.0));
Int right(&scope, runtime_->newInt(2));
Object result(&scope, runBuiltin(METH(float, __truediv__), left, right));
EXPECT_TRUE(isFloatEqualsDouble(*result, 1.5));
}
TEST_F(FloatBuiltinsTest, DunderTrueDivWithNonFloatSelfRaisesTypeError) {
HandleScope scope(thread_);
Object left(&scope, NoneType::object());
Float right(&scope, runtime_->newFloat(1.0));
Object result(&scope, runBuiltin(METH(float, __truediv__), left, right));
EXPECT_TRUE(raised(*result, LayoutId::kTypeError));
}
TEST_F(FloatBuiltinsTest, DunderTrueDivWithNonFloatOtherReturnsNotImplemented) {
HandleScope scope(thread_);
Float left(&scope, runtime_->newFloat(1.0));
Object right(&scope, NoneType::object());
Object result(&scope, runBuiltin(METH(float, __truediv__), left, right));
EXPECT_TRUE(result.isNotImplementedType());
}
TEST_F(FloatBuiltinsTest, DunderTrueDivWithZeroFloatRaisesZeroDivisionError) {
HandleScope scope(thread_);
Float left(&scope, runtime_->newFloat(1.0));
Float right(&scope, runtime_->newFloat(0.0));
Object result(&scope, runBuiltin(METH(float, __truediv__), left, right));
EXPECT_TRUE(raised(*result, LayoutId::kZeroDivisionError));
}
TEST_F(FloatBuiltinsTest,
DunderTrueDivWithZeroSmallIntRaisesZeroDivisionError) {
HandleScope scope(thread_);
Float left(&scope, runtime_->newFloat(1.0));
Int right(&scope, runtime_->newInt(0));
Object result(&scope, runBuiltin(METH(float, __truediv__), left, right));
EXPECT_TRUE(raised(*result, LayoutId::kZeroDivisionError));
}
TEST_F(FloatBuiltinsTest, DunderTrueDivWithZeroBoolRaisesZeroDivisionError) {
HandleScope scope(thread_);
Float left(&scope, runtime_->newFloat(1.0));
Bool right(&scope, RawBool::falseObj());
Object result(&scope, runBuiltin(METH(float, __truediv__), left, right));
EXPECT_TRUE(raised(*result, LayoutId::kZeroDivisionError));
}
TEST_F(FloatBuiltinsTest, DunderRtruedivWithDoubleReturnsDouble) {
HandleScope scope(thread_);
Float left(&scope, runtime_->newFloat(2.0));
Float right(&scope, runtime_->newFloat(3.0));
Object result(&scope, runBuiltin(METH(float, __rtruediv__), left, right));
EXPECT_TRUE(isFloatEqualsDouble(*result, 1.5));
}
TEST_F(FloatBuiltinsTest, DunderRtruedivWithSmallIntReturnsDouble) {
HandleScope scope(thread_);
Float left(&scope, runtime_->newFloat(2.0));
Int right(&scope, runtime_->newInt(3));
Object result(&scope, runBuiltin(METH(float, __rtruediv__), left, right));
EXPECT_TRUE(isFloatEqualsDouble(*result, 1.5));
}
TEST_F(FloatBuiltinsTest, DunderRtruedivWithNonFloatSelfRaisesTypeError) {
HandleScope scope(thread_);
Object left(&scope, NoneType::object());
Float right(&scope, runtime_->newFloat(1.0));
Object result(&scope, runBuiltin(METH(float, __rtruediv__), left, right));
EXPECT_TRUE(raised(*result, LayoutId::kTypeError));
}
TEST_F(FloatBuiltinsTest,
DunderRtruedivWithNonFloatOtherReturnsNotImplemented) {
HandleScope scope(thread_);
Float left(&scope, runtime_->newFloat(1.0));
Object right(&scope, NoneType::object());
Object result(&scope, runBuiltin(METH(float, __rtruediv__), left, right));
EXPECT_TRUE(result.isNotImplementedType());
}
TEST_F(FloatBuiltinsTest, DunderRtruedivWithZeroFloatRaisesZeroDivisionError) {
HandleScope scope(thread_);
Float left(&scope, runtime_->newFloat(0.0));
Float right(&scope, runtime_->newFloat(1.0));
Object result(&scope, runBuiltin(METH(float, __rtruediv__), left, right));
EXPECT_TRUE(raised(*result, LayoutId::kZeroDivisionError));
}
TEST_F(FloatBuiltinsTest, BinarySubtractDouble) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
a = 2.0
b = 1.5
c = a - b
)")
.isError());
Object c(&scope, mainModuleAt(runtime_, "c"));
EXPECT_TRUE(isFloatEqualsDouble(*c, 0.5));
}
TEST_F(FloatBuiltinsTest, BinarySubtractSmallInt) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
a = 2.5
b = 1
c = a - b
)")
.isError());
Object c(&scope, mainModuleAt(runtime_, "c"));
EXPECT_TRUE(isFloatEqualsDouble(*c, 1.5));
}
TEST_F(FloatBuiltinsTest, FloatSubclassKeepsFloatInMro) {
const char* src = R"(
class Test(float):
pass
)";
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, src).isError());
Object value(&scope, mainModuleAt(runtime_, "Test"));
ASSERT_TRUE(value.isType());
Type type(&scope, *value);
ASSERT_TRUE(type.mro().isTuple());
Tuple mro(&scope, type.mro());
ASSERT_EQ(mro.length(), 3);
EXPECT_EQ(mro.at(0), *type);
EXPECT_EQ(mro.at(1), runtime_->typeAt(LayoutId::kFloat));
EXPECT_EQ(mro.at(2), runtime_->typeAt(LayoutId::kObject));
}
TEST_F(FloatBuiltinsTest, PowFloatAndFloat) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
base = 2.0
x = base ** 4.0
)")
.isError());
EXPECT_TRUE(isFloatEqualsDouble(mainModuleAt(runtime_, "x"), 16.0));
}
TEST_F(FloatBuiltinsTest, PowFloatAndInt) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
base = 2.0
x = base ** 4
)")
.isError());
EXPECT_TRUE(isFloatEqualsDouble(mainModuleAt(runtime_, "x"), 16.0));
}
TEST_F(FloatBuiltinsTest, InplacePowFloatAndFloat) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
x = 2.0
x **= 4.0
)")
.isError());
EXPECT_TRUE(isFloatEqualsDouble(mainModuleAt(runtime_, "x"), 16.0));
}
TEST_F(FloatBuiltinsTest, InplacePowFloatAndInt) {
ASSERT_FALSE(runFromCStr(runtime_, R"(
x = 2.0
x **= 4
)")
.isError());
EXPECT_TRUE(isFloatEqualsDouble(mainModuleAt(runtime_, "x"), 16.0));
}
TEST_F(FloatBuiltinsTest, SubWithNonFloatOtherRaisesTypeError) {
const char* src = R"(
1.0 - None
)";
EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, src), LayoutId::kTypeError,
"float.__sub__(NoneType) is not supported"));
}
TEST_F(FloatBuiltinsTest, DunderEqWithFloatsReturnsBool) {
HandleScope scope(thread_);
Object nan(&scope, runtime_->newFloat(kDoubleNaN));
Object f0(&scope, runtime_->newFloat(1.0));
Object f1(&scope, runtime_->newFloat(-42.5));
Object zero(&scope, runtime_->newFloat(0.0));
Object neg_zero(&scope, runtime_->newFloat(-0.0));
Object null(&scope, runtime_->newInt(0));
EXPECT_EQ(runBuiltin(METH(float, __eq__), f0, f0), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __eq__), f0, f1), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __eq__), nan, nan), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __eq__), zero, neg_zero), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __eq__), neg_zero, null), Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderEqWithIntSubclassReturnsBool) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
class C(int): pass
zero = C()
one = C(1)
two = C(2)
)")
.isError());
Object self(&scope, runtime_->newFloat(1.0));
Object zero(&scope, mainModuleAt(runtime_, "zero"));
Object one(&scope, mainModuleAt(runtime_, "one"));
Object two(&scope, mainModuleAt(runtime_, "two"));
EXPECT_EQ(runBuiltin(METH(float, __eq__), self, zero), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __eq__), self, one), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __eq__), self, two), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderEqWithSmallIntExactReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(31.0));
Object float1(&scope, runtime_->newFloat(31.125));
Object int0(&scope, runtime_->newFloat(31));
EXPECT_EQ(runBuiltin(METH(float, __eq__), float0, int0), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __eq__), float1, int0), Bool::falseObj());
word mantissa_max = (word{1} << (kDoubleMantissaBits + 1)) - 1;
Object max_float(&scope,
runtime_->newFloat(static_cast<double>(mantissa_max)));
Object max_int(&scope, runtime_->newFloat(mantissa_max));
EXPECT_EQ(runBuiltin(METH(float, __eq__), max_float, max_int),
Bool::trueObj());
Object neg_max_float(&scope,
runtime_->newFloat(static_cast<double>(-mantissa_max)));
Object neg_max_int(&scope, runtime_->newInt(-mantissa_max));
EXPECT_EQ(runBuiltin(METH(float, __eq__), neg_max_float, neg_max_int),
Bool::trueObj());
word big0 = word(1) << (kDoubleMantissaBits + 2);
ASSERT_EQ(static_cast<double>(big0), static_cast<double>(big0) + 1.0);
Object big0_float(&scope, runtime_->newFloat(static_cast<double>(big0)));
Int big0_int(&scope, runtime_->newInt(big0));
EXPECT_EQ(runBuiltin(METH(float, __eq__), big0_float, big0_int),
Bool::trueObj());
word big1 = (word(1) << (kDoubleMantissaBits + 1)) | (word(1) << 11);
ASSERT_EQ(static_cast<double>(big1), static_cast<double>(big1) + 1.0);
Object big1_float(&scope, runtime_->newFloat(static_cast<double>(big1)));
Int big1_int(&scope, runtime_->newInt(big1));
EXPECT_EQ(runBuiltin(METH(float, __eq__), big1_float, big1_int),
Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderEqWithSmallIntInexactReturnsFalse) {
HandleScope scope(thread_);
word big = (word(1) << (kDoubleMantissaBits + 4)) + 3;
ASSERT_EQ(static_cast<double>(big), static_cast<double>(big) + 3.0);
Object big_float(&scope, runtime_->newFloat(static_cast<double>(big)));
Int big_int(&scope, runtime_->newInt(big));
EXPECT_EQ(runBuiltin(METH(float, __eq__), big_float, big_int),
Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderEqWithLargeIntExactReturnsTrue) {
HandleScope scope(thread_);
const uword digits[] = {0, 1};
Object int0(&scope, runtime_->newLargeIntWithDigits(digits));
Object float0(&scope, runtime_->newFloat(std::strtod("0x1p64", nullptr)));
EXPECT_EQ(runBuiltin(METH(float, __eq__), float0, int0), Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderEqWithLargeIntInexactReturnsFalse) {
HandleScope scope(thread_);
const uword digits[] = {0x800, 1};
Object int0(&scope, runtime_->newLargeIntWithDigits(digits));
Object float0(&scope, runtime_->newFloat(std::strtod("0x1p64", nullptr)));
ASSERT_EQ(Float::cast(runBuiltin(METH(int, __float__), int0)).value(),
Float::cast(*float0).value());
EXPECT_EQ(runBuiltin(METH(float, __eq__), float0, int0), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderEqWithNonFiniteFloatIntReturnsFalse) {
HandleScope scope(thread_);
Object nan(&scope, runtime_->newFloat(kDoubleNaN));
Object inf(&scope,
runtime_->newFloat(std::numeric_limits<double>::infinity()));
Object int0(&scope, runtime_->newInt(7));
uword digits[100] = {};
digits[99] = 1;
Object int1(&scope, runtime_->newLargeIntWithDigits(digits));
EXPECT_EQ(runBuiltin(METH(float, __eq__), nan, int0), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __eq__), inf, int0), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __eq__), nan, int1), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __eq__), inf, int1), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderEqWithFloatOverflowingIntReturnsFalse) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(8.25));
uword digits[100] = {};
digits[99] = 1;
Object int0(&scope, runtime_->newLargeIntWithDigits(digits));
EXPECT_EQ(runBuiltin(METH(float, __eq__), float0, int0), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderFloatWithFloatLiteralReturnsSameObject) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, "a = (7.0).__float__()").isError());
Object a(&scope, mainModuleAt(runtime_, "a"));
EXPECT_TRUE(isFloatEqualsDouble(*a, 7.0));
}
TEST_F(FloatBuiltinsTest, DunderFloatFromFloatClassReturnsSameValue) {
HandleScope scope(thread_);
Float a_float(&scope, runtime_->newFloat(7.0));
Object a(&scope, runBuiltin(METH(float, __float__), a_float));
EXPECT_TRUE(isFloatEqualsDouble(*a, 7.0));
}
TEST_F(FloatBuiltinsTest, DunderFloatWithFloatSubclassReturnsSameValue) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
class FloatSub(float):
pass
a = FloatSub(1.0).__float__())")
.isError());
Object a(&scope, mainModuleAt(runtime_, "a"));
EXPECT_TRUE(isFloatEqualsDouble(*a, 1.0));
}
TEST_F(FloatBuiltinsTest, DunderFloatWithNonFloatReturnsError) {
HandleScope scope(thread_);
Int i(&scope, runtime_->newInt(1));
Object i_res(&scope, runBuiltin(METH(float, __float__), i));
EXPECT_TRUE(raised(*i_res, LayoutId::kTypeError));
}
TEST_F(FloatBuiltinsTest, DunderGeWithFloatReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(1.7));
Object float1(&scope, runtime_->newFloat(0.2));
EXPECT_EQ(runBuiltin(METH(float, __ge__), float0, float1), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __ge__), float0, float0), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __ge__), float1, float0), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderGeWithIntSelfNanReturnsFalse) {
HandleScope scope(thread_);
Object left(&scope, runtime_->newFloat(kDoubleNaN));
const uword digits[] = {0, 1};
Object right(&scope, runtime_->newLargeIntWithDigits(digits));
EXPECT_EQ(runBuiltin(METH(float, __ge__), left, right), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderGeWithNonFloatReturnsNotImplemented) {
HandleScope scope(thread_);
Object left(&scope, runtime_->newFloat(0.0));
Object right(&scope, Str::empty());
EXPECT_TRUE(
runBuiltin(METH(float, __ge__), left, right).isNotImplementedType());
}
TEST_F(FloatBuiltinsTest, DunderGeWithSmallIntReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(5.0));
Object int0(&scope, runtime_->newInt(4));
Object int1(&scope, runtime_->newInt(5));
Object int2(&scope, runtime_->newInt(6));
EXPECT_EQ(runBuiltin(METH(float, __ge__), float0, int0), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __ge__), float0, int1), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __ge__), float0, int2), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderGeWithSmallIntExactReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(44.));
Object int0(&scope, runtime_->newInt(44));
EXPECT_EQ(runBuiltin(METH(float, __ge__), float0, int0), Bool::trueObj());
Object float1(&scope, runtime_->newFloat(-3.));
Object int1(&scope, runtime_->newInt(1));
EXPECT_EQ(runBuiltin(METH(float, __ge__), float1, int1), Bool::falseObj());
Object float2(&scope, runtime_->newFloat(0x20000000000000));
Object int2(&scope, runtime_->newInt(0x20000000000000));
EXPECT_EQ(runBuiltin(METH(float, __ge__), float2, int2), Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderGeWithSmallIntInexactReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(0x20000000000001));
Object int0(&scope, runtime_->newInt(0x20000000000001));
EXPECT_EQ(runBuiltin(METH(float, __ge__), float0, int0), Bool::falseObj());
Object float1(&scope, runtime_->newFloat(0x20000000000003));
Object int1(&scope, runtime_->newInt(0x20000000000003));
EXPECT_EQ(runBuiltin(METH(float, __ge__), float1, int1), Bool::trueObj());
Object float2(&scope, runtime_->newFloat(0x100000000000011));
Object int2(&scope, runtime_->newInt(0x100000000000011));
EXPECT_EQ(runBuiltin(METH(float, __ge__), float2, int2), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderGeWithLargeIntDifferingSignReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(-1.0));
const uword digits0[] = {0, 1};
Object int0(&scope, runtime_->newLargeIntWithDigits(digits0));
EXPECT_EQ(runBuiltin(METH(float, __ge__), float0, int0), Bool::falseObj());
Object float1(&scope, runtime_->newFloat(1.0));
const uword digits1[] = {0, kMaxUword};
Object int1(&scope, runtime_->newLargeIntWithDigits(digits1));
EXPECT_EQ(runBuiltin(METH(float, __ge__), float1, int1), Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderGeWithLargeIntExactEqualsReturnsTrue) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(std::strtod("0x1p64", nullptr)));
const uword digits[] = {0, 1};
Object int0(&scope, runtime_->newLargeIntWithDigits(digits));
EXPECT_EQ(runBuiltin(METH(float, __ge__), float0, int0), Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderGeWithLargeIntRoundingDownReturnsFalse) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(std::strtod("0x1p64", nullptr)));
const uword digits[] = {1, 1};
Object int0(&scope, runtime_->newLargeIntWithDigits(digits));
ASSERT_EQ(Float::cast(runBuiltin(METH(int, __float__), int0)).value(),
Float::cast(*float0).value());
EXPECT_EQ(runBuiltin(METH(float, __ge__), float0, int0), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderGeWithLargeIntRoundingUpReturnsTrue) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(std::strtod("0x1p64", nullptr)));
const uword digits[] = {kMaxUword, 0};
Object int0(&scope, runtime_->newLargeIntWithDigits(digits));
ASSERT_EQ(Float::cast(runBuiltin(METH(int, __float__), int0)).value(),
Float::cast(*float0).value());
EXPECT_EQ(runBuiltin(METH(float, __ge__), float0, int0), Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderGeWithIntSubclassReturnsBool) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
class C(int): pass
zero = C()
one = C(1)
two = C(2)
)")
.isError());
Object self(&scope, runtime_->newFloat(1.0));
Object zero(&scope, mainModuleAt(runtime_, "zero"));
Object one(&scope, mainModuleAt(runtime_, "one"));
Object two(&scope, mainModuleAt(runtime_, "two"));
EXPECT_EQ(runBuiltin(METH(float, __ge__), self, zero), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __ge__), self, one), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __ge__), self, two), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderGtWithFloatReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(8.3));
Object float1(&scope, runtime_->newFloat(1.7));
EXPECT_EQ(runBuiltin(METH(float, __gt__), float0, float1), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __gt__), float0, float0), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __gt__), float1, float0), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderGtWithIntSelfNanReturnsFalse) {
HandleScope scope(thread_);
Object left(&scope, runtime_->newFloat(kDoubleNaN));
const uword digits[] = {0, 1};
Object right(&scope, runtime_->newLargeIntWithDigits(digits));
EXPECT_EQ(runBuiltin(METH(float, __gt__), left, right), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderGtWithNonFloatReturnsNotImplemented) {
HandleScope scope(thread_);
Object left(&scope, runtime_->newFloat(0.0));
Object right(&scope, Str::empty());
EXPECT_TRUE(
runBuiltin(METH(float, __gt__), left, right).isNotImplementedType());
}
TEST_F(FloatBuiltinsTest, DunderGtWithSmallIntReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(5.0));
Object int0(&scope, runtime_->newInt(4));
Object int1(&scope, runtime_->newInt(5));
EXPECT_EQ(runBuiltin(METH(float, __gt__), float0, int0), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __gt__), float0, int1), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderGtWithIntSubclassReturnsBool) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
class C(int): pass
zero = C()
one = C(1)
two = C(2)
)")
.isError());
Object self(&scope, runtime_->newFloat(1.0));
Object zero(&scope, mainModuleAt(runtime_, "zero"));
Object one(&scope, mainModuleAt(runtime_, "one"));
Object two(&scope, mainModuleAt(runtime_, "two"));
EXPECT_EQ(runBuiltin(METH(float, __gt__), self, zero), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __gt__), self, one), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __gt__), self, two), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderIntWithInfinityRaisesOverflowError) {
HandleScope scope(thread_);
Object input_obj(&scope,
runtime_->newFloat(std::numeric_limits<double>::infinity()));
Object result_obj(&scope, runBuiltin(METH(float, __int__), input_obj));
EXPECT_TRUE(raisedWithStr(*result_obj, LayoutId::kOverflowError,
"cannot convert float infinity to integer"));
}
TEST_F(FloatBuiltinsTest, DunderIntWithNaNRaisesOverflowError) {
HandleScope scope(thread_);
Object input_obj(&scope, runtime_->newFloat(kDoubleNaN));
Object result_obj(&scope, runBuiltin(METH(float, __int__), input_obj));
EXPECT_TRUE(raisedWithStr(*result_obj, LayoutId::kValueError,
"cannot convert float NaN to integer"));
}
TEST_F(FloatBuiltinsTest, DunderIntWithZeroReturnsSmallInt) {
HandleScope scope(thread_);
Object input_obj(&scope, runtime_->newFloat(0.0));
Object result_obj(&scope, runBuiltin(METH(float, __int__), input_obj));
ASSERT_TRUE(result_obj.isSmallInt());
SmallInt result(&scope, *result_obj);
EXPECT_EQ(result.value(), 0);
}
TEST_F(
FloatBuiltinsTest,
DunderIntWithNegativeNumOfGreatestMagnitudeFitInWordReturnsLargeIntOfSingleWord) {
HandleScope scope(thread_);
double input_value = std::strtod("-0x1.0000000000000p+63", nullptr);
Object input_obj(&scope, runtime_->newFloat(input_value));
Object result_obj(&scope, runBuiltin(METH(float, __int__), input_obj));
ASSERT_TRUE(result_obj.isLargeInt());
LargeInt result(&scope, *result_obj);
EXPECT_TRUE(result.isNegative());
const uword expected_digits[] = {uword{0x8000000000000000}};
EXPECT_TRUE(isIntEqualsDigits(*result, expected_digits));
}
TEST_F(FloatBuiltinsTest, DunderIntWithSmallIntMinValueReturnsSmallInt) {
HandleScope scope(thread_);
double input_value = double{SmallInt::kMinValue};
// Make sure that the converted double value can fit in SmallInt if
// it gets converted back to word.
EXPECT_EQ(static_cast<word>(input_value), SmallInt::kMinValue);
Object input_obj(&scope, runtime_->newFloat(input_value));
Object result_obj(&scope, runBuiltin(METH(float, __int__), input_obj));
EXPECT_TRUE(result_obj.isSmallInt());
SmallInt result(&scope, *result_obj);
EXPECT_EQ(result.value(), static_cast<word>(input_value));
}
TEST_F(FloatBuiltinsTest,
DunderIntWithValueLessThanSmallIntMinValueReturnsLargeInt) {
HandleScope scope(thread_);
// Due to the truncation error, static_cast<double>(SmallInt::kMinValue - i)
// == SmallInt::kMinValue for i ranging from 0 to 512.
ASSERT_EQ(static_cast<word>(static_cast<double>(SmallInt::kMinValue - 512)),
SmallInt::kMinValue);
ASSERT_LT(static_cast<word>(static_cast<double>(SmallInt::kMinValue - 513)),
SmallInt::kMinValue - 1);
double input_value = static_cast<double>(SmallInt::kMinValue) - 513;
Object input_obj(&scope, runtime_->newFloat(input_value));
Object result_obj(&scope, runBuiltin(METH(float, __int__), input_obj));
EXPECT_TRUE(result_obj.isLargeInt());
}
TEST_F(FloatBuiltinsTest, DunderIntWithSmallIntMaxValueReturnsSmallInt) {
HandleScope scope(thread_);
// Due to the truncation error, static_cast<double>(SmallInt::kMaxValue) -i)
// == SmallInt::kMaxValue + 1 for i ranging from 0 to 255, which makes them
// not fit in SmallInt.
ASSERT_EQ(static_cast<word>(static_cast<double>(SmallInt::kMaxValue - 255)),
SmallInt::kMaxValue + 1);
double input_value = static_cast<double>(SmallInt::kMaxValue - 256);
Object input_obj(&scope, runtime_->newFloat(input_value));
Object result_obj(&scope, runBuiltin(METH(float, __int__), input_obj));
EXPECT_TRUE(result_obj.isSmallInt());
SmallInt result(&scope, *result_obj);
EXPECT_EQ(result.value(), static_cast<word>(input_value));
}
TEST_F(FloatBuiltinsTest,
DunderIntWithValueGreaterThanSmallIntMaxValueReturnsLargeInt) {
HandleScope scope(thread_);
// Due to the truncation error, converting MaxValue to double strictly
// increases the value.
ASSERT_GT(static_cast<word>(static_cast<double>(SmallInt::kMaxValue)),
SmallInt::kMaxValue);
// Therefore, this is the smallest double greater than kMaxValue.
double input_value = static_cast<double>(SmallInt::kMaxValue);
Object input_obj(&scope, runtime_->newFloat(input_value));
Object result_obj(&scope, runBuiltin(METH(float, __int__), input_obj));
EXPECT_TRUE(result_obj.isLargeInt());
}
TEST_F(FloatBuiltinsTest, DunderIntWithLargePositiveDoubleReturnsLargeInt) {
HandleScope scope(thread_);
double input_value = std::strtod("0x1.29ef685b3f6fbp+84", nullptr);
Object input_obj(&scope, runtime_->newFloat(input_value));
Object result_obj(&scope, runBuiltin(METH(float, __int__), input_obj));
ASSERT_TRUE(result_obj.isLargeInt());
LargeInt result(&scope, *result_obj);
EXPECT_TRUE(result.isPositive());
const uword expected_digits[] = {0x85b3f6fb00000000, 0x129ef6};
EXPECT_TRUE(isIntEqualsDigits(*result, expected_digits));
}
TEST_F(FloatBuiltinsTest, DunderIntWithLargeNegativeDoubleReturnsLargeInt) {
HandleScope scope(thread_);
double input_value = std::strtod("-0x1.29ef685b3f6fbp+84", nullptr);
Object input_obj(&scope, runtime_->newFloat(input_value));
Object result_obj(&scope, runBuiltin(METH(float, __int__), input_obj));
ASSERT_TRUE(result_obj.isLargeInt());
LargeInt result(&scope, *result_obj);
EXPECT_TRUE(result.isNegative());
// Represented as a two's complement, so 1 is added only to the lowest digit
// as long as it doesn't creat a carry.
const uword expected_digits[] = {~uword{0x85b3f6fb00000000} + 1,
~uword{0x129ef6}};
EXPECT_TRUE(isIntEqualsDigits(*result, expected_digits));
}
TEST_F(FloatBuiltinsTest, DunderLeWithFloatReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(13.1));
Object float1(&scope, runtime_->newFloat(9.4));
EXPECT_EQ(runBuiltin(METH(float, __le__), float0, float1), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __le__), float0, float0), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __le__), float1, float0), Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderLeWithIntSelfNanReturnsFalse) {
HandleScope scope(thread_);
Object left(&scope, runtime_->newFloat(kDoubleNaN));
const uword digits[] = {0, 1};
Object right(&scope, runtime_->newLargeIntWithDigits(digits));
EXPECT_EQ(runBuiltin(METH(float, __le__), left, right), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderLeWithNonFloatReturnsNotImplemented) {
HandleScope scope(thread_);
Object left(&scope, runtime_->newFloat(0.0));
Object right(&scope, Str::empty());
EXPECT_TRUE(
runBuiltin(METH(float, __le__), left, right).isNotImplementedType());
}
TEST_F(FloatBuiltinsTest, DunderLeWithSmallIntReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(4.0));
Object int0(&scope, runtime_->newInt(4));
Object int1(&scope, runtime_->newInt(3));
EXPECT_EQ(runBuiltin(METH(float, __le__), float0, int0), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __le__), float0, int1), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderLeWithBoolReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(1.0));
Object b_false(&scope, Bool::falseObj());
Object b_true(&scope, Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __le__), float0, b_false), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __le__), float0, b_true), Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderLeWithIntSubclassReturnsBool) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
class C(int): pass
zero = C()
one = C(1)
two = C(2)
)")
.isError());
Object self(&scope, runtime_->newFloat(1.0));
Object zero(&scope, mainModuleAt(runtime_, "zero"));
Object one(&scope, mainModuleAt(runtime_, "one"));
Object two(&scope, mainModuleAt(runtime_, "two"));
EXPECT_EQ(runBuiltin(METH(float, __le__), self, zero), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __le__), self, one), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __le__), self, two), Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderLtWithFloatReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(-7.3));
Object float1(&scope, runtime_->newFloat(1.25));
EXPECT_EQ(runBuiltin(METH(float, __lt__), float0, float1), Bool::trueObj());
EXPECT_EQ(runBuiltin(METH(float, __lt__), float0, float0), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __lt__), float1, float0), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderLtWithIntSelfNanReturnsFalse) {
HandleScope scope(thread_);
Object left(&scope, runtime_->newFloat(kDoubleNaN));
const uword digits[] = {0, 1};
Object right(&scope, runtime_->newLargeIntWithDigits(digits));
EXPECT_EQ(runBuiltin(METH(float, __lt__), left, right), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderLtWithNonFloatReturnsNotImplemented) {
HandleScope scope(thread_);
Object left(&scope, runtime_->newFloat(0.0));
Object right(&scope, Str::empty());
EXPECT_TRUE(
runBuiltin(METH(float, __lt__), left, right).isNotImplementedType());
}
TEST_F(FloatBuiltinsTest, DunderLtWithSmallIntReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(4.5));
Object int0(&scope, runtime_->newInt(4));
Object int1(&scope, runtime_->newInt(5));
EXPECT_EQ(runBuiltin(METH(float, __lt__), float0, int0), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __lt__), float0, int1), Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderLtWithSmallIntExactReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(44.));
Object int0(&scope, runtime_->newInt(44));
EXPECT_EQ(runBuiltin(METH(float, __lt__), float0, int0), Bool::falseObj());
Object float1(&scope, runtime_->newFloat(-3.));
Object int1(&scope, runtime_->newInt(1));
EXPECT_EQ(runBuiltin(METH(float, __lt__), float1, int1), Bool::trueObj());
Object float2(&scope, runtime_->newFloat(0x20000000000000));
Object int2(&scope, runtime_->newInt(0x20000000000000));
EXPECT_EQ(runBuiltin(METH(float, __lt__), float2, int2), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderLtWithSmallIntInexactReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(0x20000000000001));
Object int0(&scope, runtime_->newInt(0x20000000000001));
EXPECT_EQ(runBuiltin(METH(float, __lt__), float0, int0), Bool::trueObj());
Object float1(&scope, runtime_->newFloat(0x20000000000003));
Object int1(&scope, runtime_->newInt(0x20000000000003));
EXPECT_EQ(runBuiltin(METH(float, __lt__), float1, int1), Bool::falseObj());
Object float2(&scope, runtime_->newFloat(0x100000000000011));
Object int2(&scope, runtime_->newInt(0x100000000000011));
EXPECT_EQ(runBuiltin(METH(float, __lt__), float2, int2), Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderLtWithLargeIntDifferingSignReturnsBool) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(-1.0));
const uword digits0[] = {0, 1};
Object int0(&scope, runtime_->newLargeIntWithDigits(digits0));
EXPECT_EQ(runBuiltin(METH(float, __lt__), float0, int0), Bool::trueObj());
Object float1(&scope, runtime_->newFloat(1.0));
const uword digits1[] = {0, kMaxUword};
Object int1(&scope, runtime_->newLargeIntWithDigits(digits1));
EXPECT_EQ(runBuiltin(METH(float, __lt__), float1, int1), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderLtWithLargeIntExactEqualsReturnsFalse) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(std::strtod("0x1p64", nullptr)));
const uword digits[] = {0, 1};
Object int0(&scope, runtime_->newLargeIntWithDigits(digits));
EXPECT_EQ(runBuiltin(METH(float, __lt__), float0, int0), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderLtWithLargeIntRoundingDownReturnsTrue) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(std::strtod("0x1p64", nullptr)));
const uword digits[] = {1, 1};
Object int0(&scope, runtime_->newLargeIntWithDigits(digits));
ASSERT_EQ(Float::cast(runBuiltin(METH(int, __float__), int0)).value(),
Float::cast(*float0).value());
EXPECT_EQ(runBuiltin(METH(float, __lt__), float0, int0), Bool::trueObj());
}
TEST_F(FloatBuiltinsTest, DunderLtWithLargeIntRoundingUpReturnsFalse) {
HandleScope scope(thread_);
Object float0(&scope, runtime_->newFloat(std::strtod("0x1p64", nullptr)));
const uword digits[] = {kMaxUword, 0};
Object int0(&scope, runtime_->newLargeIntWithDigits(digits));
ASSERT_EQ(Float::cast(runBuiltin(METH(int, __float__), int0)).value(),
Float::cast(*float0).value());
EXPECT_EQ(runBuiltin(METH(float, __lt__), float0, int0), Bool::falseObj());
}
TEST_F(FloatBuiltinsTest, DunderLtWithIntSubclassReturnsBool) {
HandleScope scope(thread_);
ASSERT_FALSE(runFromCStr(runtime_, R"(
class C(int): pass
zero = C()
one = C(1)
two = C(2)
)")
.isError());
Object self(&scope, runtime_->newFloat(1.0));
Object zero(&scope, mainModuleAt(runtime_, "zero"));
Object one(&scope, mainModuleAt(runtime_, "one"));
Object two(&scope, mainModuleAt(runtime_, "two"));
EXPECT_EQ(runBuiltin(METH(float, __lt__), self, zero), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __lt__), self, one), Bool::falseObj());
EXPECT_EQ(runBuiltin(METH(float, __lt__), self, two), Bool::trueObj());
}
} // namespace testing
} // namespace py