runtime/runtime-test.cpp (3,055 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) #include "runtime.h" #include <csignal> #include <cstdlib> #include <memory> #include "gtest/gtest.h" #include "bytecode.h" #include "bytes-builtins.h" #include "capi.h" #include "dict-builtins.h" #include "file.h" #include "frame.h" #include "int-builtins.h" #include "layout.h" #include "memoryview-builtins.h" #include "module-builtins.h" #include "object-builtins.h" #include "set-builtins.h" #include "str-builtins.h" #include "symbols.h" #include "test-utils.h" #include "trampolines.h" #include "type-builtins.h" namespace py { namespace testing { using RuntimeAttributeTest = RuntimeFixture; using RuntimeBytearrayTest = RuntimeFixture; using RuntimeBytesTest = RuntimeFixture; using RuntimeClassAttrTest = RuntimeFixture; using RuntimeFunctionAttrTest = RuntimeFixture; using RuntimeInstanceAttrTest = RuntimeFixture; using RuntimeIntTest = RuntimeFixture; using RuntimeListTest = RuntimeFixture; using RuntimeMetaclassTest = RuntimeFixture; using RuntimeModuleAttrTest = RuntimeFixture; using RuntimeModuleTest = RuntimeFixture; using RuntimeSetTest = RuntimeFixture; using RuntimeStrArrayTest = RuntimeFixture; using RuntimeStrTest = RuntimeFixture; using RuntimeTest = RuntimeFixture; using RuntimeTupleTest = RuntimeFixture; using RuntimeTypeCallTest = RuntimeFixture; RawObject makeTestFunction() { Thread* thread = Thread::current(); HandleScope scope(thread); Runtime* runtime = thread->runtime(); Object obj(&scope, NoneType::object()); Tuple consts(&scope, runtime->newTupleWith1(obj)); const byte bytecode[] = {LOAD_CONST, 0, RETURN_VALUE, 0}; Code code(&scope, newCodeWithBytesConsts(bytecode, consts)); Object qualname(&scope, runtime->newStrFromCStr("foo")); Module module(&scope, findMainModule(runtime)); return runtime->newFunctionWithCode(thread, qualname, code, module); } TEST_F(RuntimeTest, CollectGarbage) { ASSERT_TRUE(runtime_->heap()->verify()); runtime_->collectGarbage(); ASSERT_TRUE(runtime_->heap()->verify()); } TEST_F(RuntimeTest, ComputeBuiltinBaseReturnsMostSpecificBase) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C(UnicodeDecodeError, LookupError): pass )") .isError()); HandleScope scope(thread_); Object c(&scope, mainModuleAt(runtime_, "C")); ASSERT_TRUE(c.isType()); EXPECT_EQ(Type::cast(*c).builtinBase(), LayoutId::kUnicodeDecodeError); } TEST_F(RuntimeTest, ComputeBuiltinBaseWithConflictingBasesRaisesTypeError) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( class FailingMultiClass(UnicodeDecodeError, UnicodeEncodeError): pass )"), LayoutId::kTypeError, "multiple bases have instance lay-out conflict")); } TEST(RuntimeTestNoFixture, AllocateAndCollectGarbage) { const word heap_size = 32 * kMiB; const word array_length = 1024; const word allocation_size = roundAllocationSize(array_length + HeapObject::headerSize(array_length)); const word total_allocation_size = heap_size * 10; RandomState random_seed = randomStateFromSeed(0); std::unique_ptr<Runtime> runtime( new Runtime(heap_size, createCppInterpreter(), random_seed)); ASSERT_TRUE(runtime->heap()->verify()); for (word i = 0; i < total_allocation_size; i += allocation_size) { runtime->newBytes(array_length, 0); } ASSERT_TRUE(runtime->heap()->verify()); } TEST_F(RuntimeTest, AttributeAtCallsDunderGetattribute) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: foo = None def __getattribute__(self, name): return (self, name) c = C() )") .isError()); Object c(&scope, mainModuleAt(runtime_, "c")); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object result_obj(&scope, runtime_->attributeAt(thread_, c, name)); ASSERT_TRUE(result_obj.isTuple()); Tuple result(&scope, *result_obj); ASSERT_EQ(result.length(), 2); EXPECT_EQ(result.at(0), c); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "foo")); } TEST_F(RuntimeTest, AttributeAtPropagatesExceptionFromDunderGetAttribute) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __getattribute__(self, name): raise UserWarning() c = C() )") .isError()); Object c(&scope, mainModuleAt(runtime_, "c")); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE( raised(runtime_->attributeAt(thread_, c, name), LayoutId::kUserWarning)); } TEST_F(RuntimeTest, AttributeAtPropagatesExceptionFromDunderGetAttributeDescr) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class Descr: def __get__(self, instance, owner): raise UserWarning("foo") class C: __getattribute__ = Descr() c = C() )") .isError()); Object c(&scope, mainModuleAt(runtime_, "c")); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE( raised(runtime_->attributeAt(thread_, c, name), LayoutId::kUserWarning)); } TEST_F(RuntimeTest, AttributeAtCallsDunderGetattr) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: foo = 10 def __getattr__(self, name): return (self, name) c = C() )") .isError()); Object c(&scope, mainModuleAt(runtime_, "c")); Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE(isIntEqualsWord(runtime_->attributeAt(thread_, c, foo), 10)); Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar")); Object result_obj(&scope, runtime_->attributeAt(thread_, c, bar)); ASSERT_TRUE(result_obj.isTuple()); Tuple result(&scope, *result_obj); ASSERT_EQ(result.length(), 2); EXPECT_EQ(result.at(0), c); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "bar")); } TEST_F(RuntimeTest, AttributeAtDoesNotCallDunderGetattrOnNonAttributeError) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __getattribute__(self, name): raise UserWarning() def __getattr__(self, name): _unimplemented() c = C() )") .isError()); Object c(&scope, mainModuleAt(runtime_, "c")); Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo")); EXPECT_TRUE( raised(runtime_->attributeAt(thread_, c, foo), LayoutId::kUserWarning)); } TEST_F(RuntimeTest, AttributeAtSetLocationSetsLocation) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __init__(self): self.foo = 42 i = C() )") .isError()); Object i(&scope, mainModuleAt(runtime_, "i")); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object to_cache(&scope, NoneType::object()); LoadAttrKind kind = LoadAttrKind::kInstanceOffset; EXPECT_TRUE(isIntEqualsWord( runtime_->attributeAtSetLocation(thread_, i, name, &kind, &to_cache), 42)); EXPECT_EQ(kind, LoadAttrKind::kInstanceOffset); EXPECT_TRUE(isIntEqualsWord( Interpreter::loadAttrWithLocation(thread_, *i, *to_cache), 42)); } TEST_F(RuntimeTest, AttributeAtSetLocationSetsLocationToProprty) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( def foo(self): return "data descriptor" class C: foo = property (foo) c = C() )") .isError()); Object c(&scope, mainModuleAt(runtime_, "c")); Str name(&scope, runtime_->newStrFromCStr("foo")); Object to_cache(&scope, NoneType::object()); LoadAttrKind kind = LoadAttrKind::kUnknown; EXPECT_TRUE(isStrEqualsCStr( runtime_->attributeAtSetLocation(thread_, c, name, &kind, &to_cache), "data descriptor")); EXPECT_EQ(kind, LoadAttrKind::kInstanceProperty); EXPECT_EQ(to_cache, mainModuleAt(runtime_, "foo")); } TEST_F( RuntimeTest, AttributeAtSetLocationSetLocationToPropertyAsDataDescriptorWithNoneGetter) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( C_foo = property (fget=None, fset=lambda self,v: None, fdel=lambda self: None) class C: foo = C_foo c = C() )") .isError()); Object c(&scope, mainModuleAt(runtime_, "c")); Str name(&scope, runtime_->newStrFromCStr("foo")); Object to_cache(&scope, NoneType::object()); LoadAttrKind kind = LoadAttrKind::kUnknown; EXPECT_TRUE( runtime_->attributeAtSetLocation(thread_, c, name, &kind, &to_cache) .isError()); EXPECT_EQ(to_cache, mainModuleAt(runtime_, "C_foo")); EXPECT_EQ(kind, LoadAttrKind::kInstanceTypeDescr); } TEST_F(RuntimeTest, LoadAttrWithModuleSetLocationSetsLocation) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( a_global = 1234 )") .isError()); Object mod(&scope, findMainModule(runtime_)); Object name(&scope, Runtime::internStrFromCStr(thread_, "a_global")); Object to_cache(&scope, NoneType::object()); LoadAttrKind kind; ASSERT_TRUE(isIntEqualsWord( runtime_->attributeAtSetLocation(thread_, mod, name, &kind, &to_cache), 1234)); EXPECT_EQ(kind, LoadAttrKind::kModule); EXPECT_EQ(to_cache.layoutId(), LayoutId::kValueCell); } TEST_F(RuntimeTest, LoadAttrWithTypeSetLocationSetsLocation) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: an_attribute = 1234 )") .isError()); Object type(&scope, mainModuleAt(runtime_, "C")); Object name(&scope, Runtime::internStrFromCStr(thread_, "an_attribute")); Object to_cache(&scope, NoneType::object()); LoadAttrKind kind; ASSERT_TRUE(isIntEqualsWord( runtime_->attributeAtSetLocation(thread_, type, name, &kind, &to_cache), 1234)); EXPECT_EQ(kind, LoadAttrKind::kType); EXPECT_EQ(to_cache.layoutId(), LayoutId::kValueCell); } TEST_F(RuntimeTest, AttributeAtSetLocationWithCustomGetAttributeSetsNoLocation) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __getattribute__(self, name): return 11 i = C() )") .isError()); Object i(&scope, mainModuleAt(runtime_, "i")); Object name(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object to_cache(&scope, NoneType::object()); LoadAttrKind kind; EXPECT_TRUE(isIntEqualsWord( runtime_->attributeAtSetLocation(thread_, i, name, &kind, &to_cache), 11)); EXPECT_EQ(kind, LoadAttrKind::kUnknown); EXPECT_TRUE(to_cache.isNoneType()); } TEST_F(RuntimeTest, AttributeAtSetLocationCallsDunderGetattr) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __init__(self): self.foo = 42 def __getattr__(self, name): return 5 i = C() )") .isError()); Object i(&scope, mainModuleAt(runtime_, "i")); Object name(&scope, Runtime::internStrFromCStr(thread_, "bar")); Object to_cache(&scope, NoneType::object()); LoadAttrKind kind; EXPECT_TRUE(isIntEqualsWord( runtime_->attributeAtSetLocation(thread_, i, name, &kind, &to_cache), 5)); EXPECT_EQ(kind, LoadAttrKind::kUnknown); EXPECT_TRUE(to_cache.isNoneType()); } TEST_F(RuntimeTest, AttributeAtSetLocationWithNoAttributeRaisesAttributeError) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( obj = object() )") .isError()); Object obj(&scope, mainModuleAt(runtime_, "obj")); Object name(&scope, Runtime::internStrFromCStr(thread_, "nonexistent_attr")); LoadAttrKind kind; EXPECT_TRUE(raisedWithStr( runtime_->attributeAtSetLocation(thread_, obj, name, &kind, nullptr), LayoutId::kAttributeError, "'object' object has no attribute 'nonexistent_attr'")); } // Return the raw name of a builtin LayoutId, or "<invalid>" for user-defined or // invalid LayoutIds. static const char* layoutIdName(LayoutId id) { switch (id) { case LayoutId::kError: // Special-case the one type that isn't really a class so we don't have to // have it in CLASS_NAMES. return "RawError"; #define CASE(name) \ case LayoutId::k##name: \ return #name; CLASS_NAMES(CASE) #undef CASE case LayoutId::kSentinelId: return "<SentinelId>"; } return "<invalid>"; } class BuiltinTypeIdsTest : public ::testing::TestWithParam<LayoutId> {}; // Make sure that each built-in class has a class object. Check that its class // object points to a layout with the same layout ID as the built-in class. TEST_P(BuiltinTypeIdsTest, HasTypeObject) { std::unique_ptr<Runtime> runtime(createTestRuntime()); HandleScope scope(Thread::current()); LayoutId id = GetParam(); ASSERT_EQ(runtime->layoutAt(id).layoutId(), LayoutId::kLayout) << "Bad RawLayout for " << layoutIdName(id); Object elt(&scope, runtime->concreteTypeAt(id)); ASSERT_TRUE(elt.isType()); Type cls(&scope, *elt); Layout layout(&scope, cls.instanceLayout()); EXPECT_EQ(layout.id(), GetParam()); } static const LayoutId kBuiltinHeapTypeIds[] = { #define ENUM(x) LayoutId::k##x, HEAP_CLASS_NAMES(ENUM) #undef ENUM }; INSTANTIATE_TEST_SUITE_P(BuiltinTypeIdsParameters, BuiltinTypeIdsTest, ::testing::ValuesIn(kBuiltinHeapTypeIds)); TEST_F(RuntimeTest, ConcreteIntTypeBaseIsUserType) { HandleScope scope(thread_); Object smallint(&scope, SmallInt::fromWord(42)); Object largeint(&scope, runtime_->newIntFromUnsigned(kMaxUword)); Type smallint_type(&scope, runtime_->concreteTypeOf(*smallint)); Type largeint_type(&scope, runtime_->concreteTypeOf(*largeint)); EXPECT_EQ(smallint_type.instanceLayout(), runtime_->layoutAt(LayoutId::kSmallInt)); EXPECT_EQ(largeint_type.instanceLayout(), runtime_->layoutAt(LayoutId::kLargeInt)); EXPECT_EQ(smallint_type.builtinBase(), LayoutId::kInt); EXPECT_EQ(largeint_type.builtinBase(), LayoutId::kInt); } TEST_F(RuntimeTest, ConcreteBytesTypeBaseIsUserType) { HandleScope scope(thread_); byte small_src[] = "42"; byte large_src[] = "my long bytes"; Object smallbytes(&scope, SmallBytes::fromBytes(small_src)); Object largebytes(&scope, runtime_->newBytesWithAll(large_src)); Type smallbytes_type(&scope, runtime_->concreteTypeOf(*smallbytes)); Type largebytes_type(&scope, runtime_->concreteTypeOf(*largebytes)); EXPECT_EQ(smallbytes_type.instanceLayout(), runtime_->layoutAt(LayoutId::kSmallBytes)); EXPECT_EQ(largebytes_type.instanceLayout(), runtime_->layoutAt(LayoutId::kLargeBytes)); EXPECT_EQ(smallbytes_type.builtinBase(), LayoutId::kBytes); EXPECT_EQ(largebytes_type.builtinBase(), LayoutId::kBytes); } TEST_F(RuntimeTest, ConcreteStrTypeBaseIsUserType) { HandleScope scope(thread_); Object smallstr(&scope, SmallStr::fromCStr("42")); Object largestr(&scope, runtime_->newStrFromCStr("my long str")); Type smallstr_type(&scope, runtime_->concreteTypeOf(*smallstr)); Type largestr_type(&scope, runtime_->concreteTypeOf(*largestr)); EXPECT_EQ(smallstr_type.instanceLayout(), runtime_->layoutAt(LayoutId::kSmallStr)); EXPECT_EQ(largestr_type.instanceLayout(), runtime_->layoutAt(LayoutId::kLargeStr)); EXPECT_EQ(smallstr_type.builtinBase(), LayoutId::kStr); EXPECT_EQ(largestr_type.builtinBase(), LayoutId::kStr); } TEST_F(RuntimeBytearrayTest, EnsureCapacity) { HandleScope scope(thread_); Bytearray array(&scope, runtime_->newBytearray()); word length = 1; word expected_capacity = 16; runtime_->bytearrayEnsureCapacity(thread_, array, length); EXPECT_EQ(array.capacity(), expected_capacity); length = 17; expected_capacity = 24; runtime_->bytearrayEnsureCapacity(thread_, array, length); EXPECT_EQ(array.capacity(), expected_capacity); length = 40; expected_capacity = 40; runtime_->bytearrayEnsureCapacity(thread_, array, length); EXPECT_EQ(array.capacity(), expected_capacity); } TEST_F(RuntimeBytearrayTest, Extend) { HandleScope scope(thread_); Bytearray array(&scope, runtime_->newBytearray()); View<byte> hello(reinterpret_cast<const byte*>("Hello world!"), 5); runtime_->bytearrayExtend(thread_, array, hello); EXPECT_GE(array.capacity(), 5); EXPECT_EQ(array.numItems(), 5); Bytes bytes(&scope, array.items()); bytes = bytesSubseq(thread_, bytes, 0, 5); EXPECT_TRUE(isBytesEqualsCStr(bytes, "Hello")); } TEST_F(RuntimeBytesTest, BytesReplaceWithSmallBytesAndNegativeReplacesAll) { HandleScope scope(thread_); const byte src[] = {'1', '2', '2'}; Bytes bytes(&scope, runtime_->newBytesWithAll(src)); const byte in[] = {'2'}; Bytes old_bytes(&scope, runtime_->newBytesWithAll(in)); const byte out[] = {'*'}; Bytes new_bytes(&scope, runtime_->newBytesWithAll(out)); Bytes result(&scope, runtime_->bytesReplace(thread_, bytes, old_bytes, 1, new_bytes, 1, -1)); EXPECT_TRUE(isBytesEqualsCStr(result, "1**")); } TEST_F(RuntimeBytesTest, BytesReplaceWithLargeBytesAndNegativeReplacesAll) { HandleScope scope(thread_); const byte src[] = {'1', '1', '1', '1', '1', '1', '1', '2', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '1', '1'}; Bytes bytes(&scope, runtime_->newBytesWithAll(src)); const byte in[] = {'2'}; Bytes old_bytes(&scope, runtime_->newBytesWithAll(in)); const byte out[] = {'*'}; Bytes new_bytes(&scope, runtime_->newBytesWithAll(out)); Bytes result(&scope, runtime_->bytesReplace(thread_, bytes, old_bytes, 1, new_bytes, 1, -1)); EXPECT_TRUE(isBytesEqualsCStr(result, "1111111*1111111111*11")); } TEST_F(RuntimeBytesTest, BytesReplaceWithLargeBytesAndCountReplacesSome) { HandleScope scope(thread_); const byte src[] = {'1', '1', '1', '1', '1', '1', '1', '2', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '1', '1'}; Bytes bytes(&scope, runtime_->newBytesWithAll(src)); const byte in[] = {'2'}; Bytes old_bytes(&scope, runtime_->newBytesWithAll(in)); const byte out[] = {'*'}; Bytes new_bytes(&scope, runtime_->newBytesWithAll(out)); Bytes result(&scope, runtime_->bytesReplace(thread_, bytes, old_bytes, 1, new_bytes, 1, 1)); EXPECT_TRUE(isBytesEqualsCStr(result, "1111111*1111111111211")); } TEST_F(RuntimeBytesTest, BytesReplaceWithLongerNewReturnsLonger) { HandleScope scope(thread_); const byte src[] = {'1', '2'}; Bytes bytes(&scope, runtime_->newBytesWithAll(src)); const byte in[] = {'2'}; Bytes old_bytes(&scope, runtime_->newBytesWithAll(in)); const byte out[] = {'*', '*'}; Bytes new_bytes(&scope, runtime_->newBytesWithAll(out)); Bytes result(&scope, runtime_->bytesReplace(thread_, bytes, old_bytes, 1, new_bytes, 2, -1)); EXPECT_TRUE(isBytesEqualsCStr(result, "1**")); } TEST_F(RuntimeBytesTest, BytesReplaceWithShorterNewReturnsShorter) { HandleScope scope(thread_); const byte src[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1'}; Bytes bytes(&scope, runtime_->newBytesWithAll(src)); const byte in[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1'}; Bytes old_bytes(&scope, runtime_->newBytesWithAll(in)); const byte out[] = {'*'}; Bytes new_bytes(&scope, runtime_->newBytesWithAll(out)); Bytes result(&scope, runtime_->bytesReplace(thread_, bytes, old_bytes, 11, new_bytes, 1, -1)); EXPECT_TRUE(isBytesEqualsCStr(result, "*")); } TEST_F(RuntimeBytesTest, BytesReplaceWithCountZero) { HandleScope scope(thread_); const byte src[] = {'1', '2'}; Bytes bytes(&scope, runtime_->newBytesWithAll(src)); const byte in[] = {'1'}; Bytes old_bytes(&scope, runtime_->newBytesWithAll(in)); const byte out[] = {'*'}; Bytes new_bytes(&scope, runtime_->newBytesWithAll(out)); Bytes result(&scope, runtime_->bytesReplace(thread_, bytes, old_bytes, 1, new_bytes, 1, 0)); EXPECT_TRUE(isBytesEqualsCStr(result, "12")); } TEST_F(RuntimeBytesTest, BytesReplaceWithCountGreaterThanOccurences) { HandleScope scope(thread_); const byte src[] = {'1', '2'}; Bytes bytes(&scope, runtime_->newBytesWithAll(src)); const byte in[] = {'1'}; Bytes old_bytes(&scope, runtime_->newBytesWithAll(in)); const byte out[] = {'*'}; Bytes new_bytes(&scope, runtime_->newBytesWithAll(out)); Bytes result(&scope, runtime_->bytesReplace(thread_, bytes, old_bytes, 1, new_bytes, 1, 9)); EXPECT_TRUE(isBytesEqualsCStr(result, "*2")); } TEST_F(RuntimeBytesTest, Concat) { HandleScope scope(thread_); View<byte> foo(reinterpret_cast<const byte*>("foo"), 3); Bytes self(&scope, runtime_->newBytesWithAll(foo)); View<byte> bar(reinterpret_cast<const byte*>("bar"), 3); Bytes other(&scope, runtime_->newBytesWithAll(bar)); Bytes result(&scope, runtime_->bytesConcat(thread_, self, other)); EXPECT_TRUE(isBytesEqualsCStr(result, "foobar")); } TEST_F(RuntimeBytesTest, FromTupleWithSizeReturnsBytesMatchingSize) { HandleScope scope(thread_); Object obj1(&scope, SmallInt::fromWord(42)); Object obj2(&scope, SmallInt::fromWord(123)); Object obj3(&scope, SmallInt::fromWord(456)); Tuple tuple(&scope, runtime_->newTupleWith3(obj1, obj2, obj3)); Object result(&scope, runtime_->bytesFromTuple(thread_, tuple, 2)); const byte bytes[] = {42, 123}; EXPECT_TRUE(isBytesEqualsBytes(result, bytes)); } TEST_F(RuntimeBytesTest, FromTupleWithNonIndexReturnsNone) { HandleScope scope(thread_); Object obj(&scope, runtime_->newFloat(1)); Tuple tuple(&scope, runtime_->newTupleWith1(obj)); EXPECT_EQ(runtime_->bytesFromTuple(thread_, tuple, 1), NoneType::object()); } TEST_F(RuntimeBytesTest, FromTupleWithNegativeIntRaisesValueError) { HandleScope scope(thread_); Object obj(&scope, SmallInt::fromWord(-1)); Tuple tuple(&scope, runtime_->newTupleWith1(obj)); Object result(&scope, runtime_->bytesFromTuple(thread_, tuple, 1)); EXPECT_TRUE(raisedWithStr(*result, LayoutId::kValueError, "bytes must be in range(0, 256)")); } TEST_F(RuntimeBytesTest, FromTupleWithBigIntRaisesValueError) { HandleScope scope(thread_); Object obj(&scope, SmallInt::fromWord(256)); Tuple tuple(&scope, runtime_->newTupleWith1(obj)); Object result(&scope, runtime_->bytesFromTuple(thread_, tuple, 1)); EXPECT_TRUE(raisedWithStr(*result, LayoutId::kValueError, "bytes must be in range(0, 256)")); } TEST_F(RuntimeBytesTest, FromTupleWithIntSubclassReturnsBytes) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C(int): pass a = C(97) b = C(98) c = C(99) )") .isError()); HandleScope scope(thread_); Object obj1(&scope, mainModuleAt(runtime_, "a")); Object obj2(&scope, mainModuleAt(runtime_, "b")); Object obj3(&scope, mainModuleAt(runtime_, "c")); Tuple tuple(&scope, runtime_->newTupleWith3(obj1, obj2, obj3)); Object result(&scope, runtime_->bytesFromTuple(thread_, tuple, 3)); EXPECT_TRUE(isBytesEqualsCStr(result, "abc")); } TEST_F(RuntimeTest, LayoutAddAttributeReturnsExistingEdgeMatchingName) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: pass )") .isError()); HandleScope scope(thread_); Type c(&scope, mainModuleAt(runtime_, "C")); Layout instance_layout(&scope, c.instanceLayout()); Str name(&scope, runtime_->newStrFromCStr("attr")); AttributeInfo info; Layout layout(&scope, runtime_->layoutAddAttribute(thread_, instance_layout, name, 0, &info)); // Using the same name returns the same layout. EXPECT_EQ(*layout, runtime_->layoutAddAttribute(thread_, instance_layout, name, 0, &info)); // A different name creates a new layout. Str different_name(&scope, runtime_->newStrFromCStr("attr_new")); EXPECT_NE(*layout, runtime_->layoutAddAttribute(thread_, instance_layout, different_name, 0, &info)); // Using the existing layout as `name` also creates a new layout. EXPECT_NE(*layout, runtime_->layoutAddAttribute(thread_, instance_layout, layout, 0, &info)); } TEST_F(RuntimeListTest, ListGrowth) { HandleScope scope(thread_); List list(&scope, runtime_->newList()); Tuple array1(&scope, runtime_->newMutableTuple(1)); list.setItems(*array1); EXPECT_EQ(array1.length(), 1); runtime_->listEnsureCapacity(thread_, list, 2); Tuple array2(&scope, list.items()); EXPECT_NE(*array1, *array2); EXPECT_GE(array2.length(), 2); Tuple array4(&scope, runtime_->newMutableTuple(4)); list.setItems(*array4); runtime_->listEnsureCapacity(thread_, list, 5); Tuple array16(&scope, list.items()); EXPECT_NE(*array4, *array16); EXPECT_EQ(array16.length(), 16); runtime_->listEnsureCapacity(thread_, list, 17); Tuple array24(&scope, list.items()); EXPECT_NE(*array16, *array24); EXPECT_EQ(array24.length(), 24); runtime_->listEnsureCapacity(thread_, list, 40); EXPECT_EQ(list.capacity(), 40); } TEST_F(RuntimeListTest, EmptyListInvariants) { RawList list = List::cast(runtime_->newList()); ASSERT_EQ(list.capacity(), 0); ASSERT_EQ(list.numItems(), 0); } TEST_F(RuntimeListTest, AppendToList) { HandleScope scope(thread_); List list(&scope, runtime_->newList()); // Check that list capacity grows by 1.5 word expected_capacity[] = {16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, 36}; for (int i = 0; i < 25; i++) { Object value(&scope, SmallInt::fromWord(i)); runtime_->listAdd(thread_, list, value); ASSERT_EQ(list.capacity(), expected_capacity[i]) << i; ASSERT_EQ(list.numItems(), i + 1) << i; } // Sanity check list contents for (int i = 0; i < 25; i++) { EXPECT_TRUE(isIntEqualsWord(list.at(i), i)) << i; } } TEST_F(RuntimeTest, NewMutableBytesUninitializedReturnsMutableBytes) { HandleScope scope(thread_); Object result(&scope, runtime_->newMutableBytesUninitialized(3)); ASSERT_TRUE(result.isMutableBytes()); EXPECT_EQ(MutableBytes::cast(*result).length(), 3); } TEST_F(RuntimeTest, MutableBytesFromBytesWithSmallBytes) { HandleScope scope(thread_); const byte data[] = {0x11, 0x22, 0x33}; Bytes src(&scope, runtime_->newBytesWithAll(data)); ASSERT_TRUE(src.isSmallBytes()); MutableBytes dst(&scope, runtime_->mutableBytesFromBytes(thread_, src)); EXPECT_EQ(dst.length(), 3); EXPECT_EQ(dst.byteAt(0), 0x11); EXPECT_EQ(dst.byteAt(1), 0x22); EXPECT_EQ(dst.byteAt(2), 0x33); } TEST_F(RuntimeTest, MutableBytesFromBytesWithLargeBytes) { HandleScope scope(thread_); MutableBytes mutable_bytes(&scope, runtime_->newMutableBytesUninitialized(8)); mutable_bytes.byteAtPut(0, 0x11); mutable_bytes.byteAtPut(1, 0x22); mutable_bytes.byteAtPut(2, 0x33); mutable_bytes.byteAtPut(3, 0x44); mutable_bytes.byteAtPut(4, 0x55); mutable_bytes.byteAtPut(5, 0x66); mutable_bytes.byteAtPut(6, 0x77); mutable_bytes.byteAtPut(7, 0x88); Bytes src(&scope, mutable_bytes.becomeImmutable()); ASSERT_TRUE(src.isLargeBytes()); MutableBytes dst(&scope, runtime_->mutableBytesFromBytes(thread_, src)); EXPECT_EQ(dst.length(), 8); EXPECT_EQ(dst.byteAt(0), 0x11); EXPECT_EQ(dst.byteAt(1), 0x22); EXPECT_EQ(dst.byteAt(2), 0x33); EXPECT_EQ(dst.byteAt(3), 0x44); EXPECT_EQ(dst.byteAt(4), 0x55); EXPECT_EQ(dst.byteAt(5), 0x66); EXPECT_EQ(dst.byteAt(6), 0x77); EXPECT_EQ(dst.byteAt(7), 0x88); } TEST_F(RuntimeTest, MutableBytesFromBytesWithMutableBytes) { HandleScope scope(thread_); MutableBytes mutable_bytes(&scope, runtime_->newMutableBytesUninitialized(3)); mutable_bytes.byteAtPut(0, 0x11); mutable_bytes.byteAtPut(1, 0x22); mutable_bytes.byteAtPut(2, 0x33); Bytes src(&scope, *mutable_bytes); ASSERT_TRUE(src.isMutableBytes()); MutableBytes dst(&scope, runtime_->mutableBytesFromBytes(thread_, src)); EXPECT_EQ(dst.length(), 3); EXPECT_EQ(dst.byteAt(0), 0x11); EXPECT_EQ(dst.byteAt(1), 0x22); EXPECT_EQ(dst.byteAt(2), 0x33); } TEST_F(RuntimeTest, NewBytearray) { HandleScope scope(thread_); Bytearray array(&scope, runtime_->newBytearray()); EXPECT_EQ(array.numItems(), 0); EXPECT_EQ(array.capacity(), 0); } TEST_F(RuntimeTest, NewBytes) { HandleScope scope(thread_); Bytes len0(&scope, Bytes::empty()); EXPECT_EQ(len0.length(), 0); Bytes len3(&scope, runtime_->newBytes(3, 9)); EXPECT_EQ(len3.length(), 3); EXPECT_EQ(len3.byteAt(0), 9); EXPECT_EQ(len3.byteAt(1), 9); EXPECT_EQ(len3.byteAt(2), 9); Bytes len254(&scope, runtime_->newBytes(254, 0)); EXPECT_EQ(len254.length(), 254); Bytes len255(&scope, runtime_->newBytes(255, 0)); EXPECT_EQ(len255.length(), 255); } TEST_F(RuntimeTest, NewBytesWithAll) { HandleScope scope(thread_); Bytes len0(&scope, runtime_->newBytesWithAll(View<byte>(nullptr, 0))); EXPECT_EQ(len0.length(), 0); const byte src1[] = {0x42}; Bytes len1(&scope, runtime_->newBytesWithAll(src1)); EXPECT_EQ(len1.length(), 1); EXPECT_EQ(len1.byteAt(0), 0x42); const byte src3[] = {0xAA, 0xBB, 0xCC}; Bytes len3(&scope, runtime_->newBytesWithAll(src3)); EXPECT_EQ(len3.length(), 3); EXPECT_EQ(len3.byteAt(0), 0xAA); EXPECT_EQ(len3.byteAt(1), 0xBB); EXPECT_EQ(len3.byteAt(2), 0xCC); } TEST_F(RuntimeTest, NewMemoryViewFromCPtrCreatesMemoryView) { HandleScope scope(thread_); word length = 5; std::unique_ptr<byte[]> memory(new byte[length]); for (word i = 0; i < length; i++) { memory[i] = i; } Object none(&scope, NoneType::object()); MemoryView view(&scope, runtime_->newMemoryViewFromCPtr(thread_, none, memory.get(), length, ReadOnly::ReadOnly)); Pointer pointer(&scope, view.buffer()); EXPECT_EQ(view.length(), length); byte* ptr = reinterpret_cast<byte*>(pointer.cptr()); EXPECT_EQ(ptr[0], 0); EXPECT_EQ(ptr[1], 1); EXPECT_EQ(ptr[2], 2); EXPECT_EQ(ptr[3], 3); EXPECT_EQ(ptr[4], 4); } TEST_F(RuntimeTest, NewMmapReturnsEmptyMmap) { HandleScope scope(thread_); Object obj(&scope, runtime_->newMmap()); ASSERT_TRUE(obj.isMmap()); Mmap mmap_obj(&scope, *obj); EXPECT_EQ(mmap_obj.isReadable(), false); EXPECT_EQ(mmap_obj.isWritable(), false); EXPECT_EQ(mmap_obj.isCopyOnWrite(), false); EXPECT_EQ(mmap_obj.data(), NoneType::object()); EXPECT_EQ(mmap_obj.fd(), NoneType::object()); } TEST_F(RuntimeTest, LargeBytesSizeRoundedUpToPointerSizeMultiple) { HandleScope scope(thread_); LargeBytes len10(&scope, runtime_->newBytes(10, 0)); EXPECT_EQ(len10.size(), roundAllocationSize(kPointerSize + 10)); LargeBytes len254(&scope, runtime_->newBytes(254, 0)); EXPECT_EQ(len254.size(), roundAllocationSize(kPointerSize + 254)); LargeBytes len255(&scope, runtime_->newBytes(255, 0)); EXPECT_EQ(len255.size(), roundAllocationSize(kPointerSize * 2 + 255)); } TEST_F(RuntimeTest, NewPointerReturnsEmptyPointer) { HandleScope scope(thread_); Object obj(&scope, runtime_->newPointer(nullptr, -1)); ASSERT_TRUE(obj.isPointer()); Pointer pointer(&scope, *obj); EXPECT_EQ(pointer.cptr(), nullptr); EXPECT_EQ(pointer.length(), -1); } TEST_F(RuntimeTest, NewStr) { HandleScope scope(thread_); Str empty0(&scope, runtime_->newStrWithAll(View<byte>(nullptr, 0))); ASSERT_TRUE(empty0.isSmallStr()); EXPECT_EQ(empty0.length(), 0); Str empty1(&scope, runtime_->newStrWithAll(View<byte>(nullptr, 0))); ASSERT_TRUE(empty1.isSmallStr()); EXPECT_EQ(*empty0, *empty1); Str empty2(&scope, runtime_->newStrFromCStr("\0")); ASSERT_TRUE(empty2.isSmallStr()); EXPECT_EQ(*empty0, *empty2); const byte bytes1[1] = {0}; Str s1(&scope, runtime_->newStrWithAll(bytes1)); ASSERT_TRUE(s1.isSmallStr()); EXPECT_EQ(s1.length(), 1); const byte bytes254[254] = {0}; Str s254(&scope, runtime_->newStrWithAll(bytes254)); EXPECT_EQ(s254.length(), 254); ASSERT_TRUE(s254.isLargeStr()); EXPECT_EQ(HeapObject::cast(*s254).size(), roundAllocationSize(kPointerSize + 254)); const byte bytes255[255] = {0}; Str s255(&scope, runtime_->newStrWithAll(bytes255)); EXPECT_EQ(s255.length(), 255); ASSERT_TRUE(s255.isLargeStr()); EXPECT_EQ(HeapObject::cast(*s255).size(), roundAllocationSize(kPointerSize * 2 + 255)); const byte bytes300[300] = {0}; Str s300(&scope, runtime_->newStrWithAll(bytes300)); ASSERT_EQ(s300.length(), 300); } TEST_F(RuntimeTest, NewStrFromFmtFormatsWord) { word x = 5; HandleScope scope(thread_); Object result(&scope, runtime_->newStrFromFmt("hello %w world", x)); EXPECT_TRUE(isStrEqualsCStr(*result, "hello 5 world")); } TEST_F(RuntimeTest, NewStrFromFmtWithStrArg) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromCStr("hello")); Object result(&scope, runtime_->newStrFromFmt("%S", &str)); EXPECT_EQ(*result, str); } TEST_F(RuntimeTest, NewStrFromFmtWithStrSubclassArg) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C(str): pass value = C("foo") )") .isError()); Object value(&scope, mainModuleAt(runtime_, "value")); Object result(&scope, runtime_->newStrFromFmt("hello %S", &value)); EXPECT_TRUE(isStrEqualsCStr(*result, "hello foo")); } TEST_F(RuntimeStrTest, NewStrFromFmtFormatsFunctionName) { HandleScope scope(thread_); Function function(&scope, newEmptyFunction()); function.setQualname(runtime_->newStrFromCStr("foo")); Object str(&scope, runtime_->newStrFromFmt("hello %F", &function)); EXPECT_TRUE(isStrEqualsCStr(*str, "hello foo")); } TEST_F(RuntimeStrTest, NewStrFromFmtFormatsTypeName) { HandleScope scope(thread_); Object obj(&scope, runtime_->newDict()); Object str(&scope, runtime_->newStrFromFmt("hello %T", &obj)); EXPECT_TRUE(isStrEqualsCStr(*str, "hello dict")); } TEST_F(RuntimeStrTest, NewStrFromFmtFormatsSymbolid) { HandleScope scope(thread_); Object str(&scope, runtime_->newStrFromFmt("hello %Y", ID(dict))); EXPECT_TRUE(isStrEqualsCStr(*str, "hello dict")); } TEST_F(RuntimeStrTest, NewStrFromFmtFormatsASCIIChar) { EXPECT_TRUE(isStrEqualsCStr(runtime_->newStrFromFmt("'%c'", 124), "'|'")); } TEST_F(RuntimeStrTest, NewStrFromFmtFormatsNonASCIIAsReplacementChar) { EXPECT_TRUE(isStrEqualsCStr(runtime_->newStrFromFmt("'%c'", kMaxASCII + 1), "'\xef\xbf\xbd'")); } TEST_F(RuntimeStrTest, NewStrFromFmtFormatsCodePoint) { EXPECT_TRUE(isStrEqualsCStr(runtime_->newStrFromFmt("'%C'", 124), "'|'")); EXPECT_TRUE(isStrEqualsCStr(runtime_->newStrFromFmt("'%C'", 0x1F40D), "'\xf0\x9f\x90\x8d'")); } TEST_F(RuntimeStrTest, NewStrFromFormatFormatsString) { EXPECT_TRUE( isStrEqualsCStr(runtime_->newStrFromFmt("'%s'", "hello"), "'hello'")); } TEST_F(RuntimeStrTest, NewStrFromFormatFormatsInt) { EXPECT_TRUE(isStrEqualsCStr(runtime_->newStrFromFmt("'%d'", -321), "'-321'")); } TEST_F(RuntimeStrTest, NewStrFromFormatFormatsFloat) { EXPECT_TRUE(isStrEqualsCStr(runtime_->newStrFromFmt("'%g'", 3.5), "'3.5'")); } TEST_F(RuntimeStrTest, NewStrFromFormatFormatsHexadecimalInt) { EXPECT_TRUE(isStrEqualsCStr(runtime_->newStrFromFmt("'%x'", 0x2AB), "'2ab'")); } TEST_F(RuntimeStrTest, NewStrFromFormatFormatsPercent) { EXPECT_TRUE(isStrEqualsCStr(runtime_->newStrFromFmt("'%%'"), "'%'")); } TEST_F(RuntimeStrTest, NewStrFromFmtFormatsReplacesNonUnicodeWithReplacement) { EXPECT_TRUE( isStrEqualsCStr(runtime_->newStrFromFmt("'%C'", -1), "'\xef\xbf\xbd'")); } TEST_F(RuntimeStrTest, NewStrWithAll) { HandleScope scope(thread_); Str str0(&scope, runtime_->newStrWithAll(View<byte>(nullptr, 0))); EXPECT_EQ(str0.length(), 0); EXPECT_TRUE(str0.equalsCStr("")); const byte bytes3[] = {'A', 'B', 'C'}; Str str3(&scope, runtime_->newStrWithAll(bytes3)); EXPECT_EQ(str3.length(), 3); EXPECT_TRUE(str3.equalsCStr("ABC")); const byte bytes10[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'}; Str str10(&scope, runtime_->newStrWithAll(bytes10)); EXPECT_EQ(str10.length(), 10); EXPECT_TRUE(str10.equalsCStr("ABCDEFGHIJ")); } TEST_F(RuntimeStrTest, NewStrFromUTF32WithZeroSizeReturnsEmpty) { HandleScope scope(thread_); int32_t str[2] = {'a', 's'}; Str empty(&scope, runtime_->newStrFromUTF32(View<int32_t>(str, 0))); EXPECT_EQ(empty.length(), 0); } TEST_F(RuntimeStrTest, NewStrFromUTF32WithLargeASCIIStringReturnsString) { HandleScope scope(thread_); int32_t str[7] = {'a', 'b', 'c', '1', '2', '3', '-'}; Str unicode(&scope, runtime_->newStrFromUTF32(View<int32_t>(str, 7))); EXPECT_EQ(unicode.length(), 7); EXPECT_TRUE(unicode.equalsCStr("abc123-")); } TEST_F(RuntimeStrTest, NewStrFromUTF32WithSmallASCIIStringReturnsString) { HandleScope scope(thread_); int32_t str[7] = {'a', 'b'}; Str unicode(&scope, runtime_->newStrFromUTF32(View<int32_t>(str, 2))); EXPECT_EQ(unicode.length(), 2); EXPECT_TRUE(unicode.equalsCStr("ab")); } TEST_F(RuntimeStrTest, NewStrFromUTF32WithSmallNonASCIIReturnsString) { HandleScope scope(thread_); const int32_t codepoints[] = {0xC4}; Str unicode(&scope, runtime_->newStrFromUTF32(codepoints)); EXPECT_TRUE(unicode.equals(Str::cast(SmallStr::fromCodePoint(0xC4)))); } TEST_F(RuntimeStrTest, NewStrFromUTF32WithLargeNonASCIIReturnsString) { HandleScope scope(thread_); const int32_t codepoints[] = {0x3041, ' ', 'c', 0xF6, 0xF6, 'l', ' ', 0x1F192}; Str unicode(&scope, runtime_->newStrFromUTF32(codepoints)); Str expected(&scope, runtime_->newStrFromCStr( "\xe3\x81\x81 c\xC3\xB6\xC3\xB6l \xF0\x9F\x86\x92")); EXPECT_TRUE(unicode.equals(*expected)); } TEST_F(RuntimeTest, HashBools) { // In CPython, False hashes to 0 and True hashes to 1. EXPECT_EQ(runtime_->hash(Bool::falseObj()), 0); EXPECT_EQ(runtime_->hash(Bool::trueObj()), 1); } TEST_F(RuntimeTest, HashLargeBytes) { HandleScope scope(thread_); // LargeBytes have their hash codes computed lazily. const byte src1[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}; LargeBytes arr1(&scope, runtime_->newBytesWithAll(src1)); EXPECT_EQ(arr1.header().hashCode(), 0); word hash1 = runtime_->hash(*arr1); EXPECT_NE(arr1.header().hashCode(), 0); EXPECT_EQ(arr1.header().hashCode(), hash1); word code1 = runtime_->bytesHash(src1); EXPECT_EQ(code1, hash1); // LargeBytes with different values should (ideally) hash differently. const byte src2[] = {0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1}; LargeBytes arr2(&scope, runtime_->newBytesWithAll(src2)); word hash2 = runtime_->hash(*arr2); EXPECT_NE(hash1, hash2); word code2 = runtime_->bytesHash(src2); EXPECT_EQ(code2, hash2); // LargeBytes with the same value should hash the same. LargeBytes arr3(&scope, runtime_->newBytesWithAll(src1)); EXPECT_NE(arr3, arr1); word hash3 = runtime_->hash(*arr3); EXPECT_EQ(hash1, hash3); } TEST_F(RuntimeTest, HashSmallInts) { // In CPython, Ints hash to themselves. EXPECT_EQ(runtime_->hash(SmallInt::fromWord(123)), 123); EXPECT_EQ(runtime_->hash(SmallInt::fromWord(456)), 456); EXPECT_EQ(runtime_->hash(SmallInt::fromWord(-1)), -2); } TEST_F(RuntimeTest, HashSingletonImmediates) { // In CPython, these objects hash to arbitrary values. word none_value = NoneType::object().raw(); EXPECT_EQ(runtime_->hash(NoneType::object()), none_value); word error_value = Error::error().raw(); EXPECT_EQ(runtime_->hash(Error::error()), error_value); } TEST_F(RuntimeTest, HashStr) { HandleScope scope(thread_); // LargeStr instances have their hash codes computed lazily. Object str1(&scope, runtime_->newStrFromCStr("testing 123")); EXPECT_EQ(HeapObject::cast(*str1).header().hashCode(), 0); word hash1 = runtime_->hash(*str1); EXPECT_NE(HeapObject::cast(*str1).header().hashCode(), 0); EXPECT_EQ(HeapObject::cast(*str1).header().hashCode(), hash1); // Str with different values should (ideally) hash differently. Str str2(&scope, runtime_->newStrFromCStr("321 testing")); word hash2 = runtime_->hash(*str2); EXPECT_NE(hash1, hash2); // Strings with the same value should hash the same. Str str3(&scope, runtime_->newStrFromCStr("testing 123")); word hash3 = runtime_->hash(*str3); EXPECT_EQ(hash1, hash3); } TEST(RuntimeTestNoFixture, InitializeRandomSetsRandomRandomRNGSeed) { word heap_size = 32 * kMiB; std::unique_ptr<Runtime> runtime0( new Runtime(heap_size, createCppInterpreter(), randomState())); uword r0 = runtime0->random(); std::unique_ptr<Runtime> runtime1( new Runtime(heap_size, createCppInterpreter(), randomState())); uword r1 = runtime1->random(); std::unique_ptr<Runtime> runtime2( new Runtime(heap_size, createCppInterpreter(), randomState())); uword r2 = runtime2->random(); // Having 3 random numbers be the same will practically never happen. EXPECT_TRUE(r0 != r1 || r0 != r2); } TEST(RuntimeTestNoFixture, InitializeRandomWithPyroHashSeedEnvVarSetsDeterministicRNGSeed) { word heap_size = 32 * kMiB; RandomState seed = randomStateFromSeed(42); std::unique_ptr<Runtime> runtime0( new Runtime(heap_size, createCppInterpreter(), seed)); uword r0_a = runtime0->random(); uword r0_b = runtime0->random(); EXPECT_NE(r0_a, r0_b); std::unique_ptr<Runtime> runtime1( new Runtime(heap_size, createCppInterpreter(), seed)); uword r1_a = runtime1->random(); uword r1_b = runtime1->random(); EXPECT_EQ(r0_a, r1_a); EXPECT_EQ(r0_b, r1_b); } TEST_F(RuntimeTest, TypeDictOnlyLayoutReturnsLayoutWithDictOverflow) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: pass c = C() )") .isError()); HandleScope scope(thread_); Type c(&scope, mainModuleAt(runtime_, "C")); Layout layout(&scope, c.instanceLayout()); word in_object_attributes_length = Tuple::cast(layout.inObjectAttributes()).length(); ASSERT_TRUE(!layout.hasDictOverflow()); Layout new_layout(&scope, runtime_->typeDictOnlyLayout( thread_, c, in_object_attributes_length)); EXPECT_NE(layout, new_layout); EXPECT_TRUE(new_layout.hasDictOverflow()); EXPECT_EQ(layout.describedType(), new_layout.describedType()); Layout new_layout2(&scope, runtime_->typeDictOnlyLayout( thread_, c, in_object_attributes_length)); EXPECT_NE(layout, new_layout2); EXPECT_TRUE(new_layout2.hasDictOverflow()); EXPECT_EQ(layout.describedType(), new_layout2.describedType()); EXPECT_EQ(*new_layout, *new_layout2); } TEST_F( RuntimeTest, TypeDictOnlyLayoutWithDunderClassAssignedInstanceReturnsLayoutBasedOnInObjectAttributesLength) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __init__(self): self.x = 10 self.y = 20 )") .isError()); HandleScope scope(thread_); Type type_c(&scope, mainModuleAt(runtime_, "C")); Layout new_layout(&scope, runtime_->typeDictOnlyLayout(thread_, type_c, 3)); EXPECT_TRUE(new_layout.hasDictOverflow()); EXPECT_EQ(new_layout.dictOverflowOffset(), 24); EXPECT_EQ(new_layout.describedType(), *type_c); // Using the same value of `in_object_attributes_length` returns the same // layout. Layout new_layout2(&scope, runtime_->typeDictOnlyLayout(thread_, type_c, 3)); EXPECT_EQ(new_layout, new_layout2); // Create a new layout for the new value of `in_object_attributes_length`. Layout new_layout3(&scope, runtime_->typeDictOnlyLayout(thread_, type_c, 3 + 1)); EXPECT_NE(new_layout, new_layout3); } TEST_F(RuntimeTest, HashCodeSizeCheck) { // Conspire based on knowledge of the random number generated to // create a high-magnitude result from Runtime::random // which is truncated to 0 for storage in the header and // replaced with "1" so no hash code has value 0. RandomState seed = randomStateFromSeed(0); uint64_t high = uword{1} << (8 * sizeof(uword) - 1); seed.state[0] = 0; seed.state[1] = high; // Verify that our crafted random seed does indeed produce the number // we expect. runtime_->setRandomState(seed); EXPECT_EQ(runtime_->random(), high); // Verify that large-magnitude random numbers are properly // truncated to something which fits in a SmallInt runtime_->setRandomState(seed); HandleScope scope(thread_); Layout layout(&scope, runtime_->layoutAt(LayoutId::kObject)); Object object(&scope, runtime_->newInstance(layout)); EXPECT_EQ(runtime_->hash(*object), 1); } TEST_F(RuntimeTest, NewCapacity) { // ensure initial capacity EXPECT_GE(Runtime::newCapacity(1, 0), 16); // grow by factor of 1.5, rounding down EXPECT_EQ(Runtime::newCapacity(20, 22), 30); EXPECT_EQ(Runtime::newCapacity(64, 77), 96); EXPECT_EQ(Runtime::newCapacity(25, 30), 37); // ensure growth EXPECT_EQ(Runtime::newCapacity(20, 17), 30); EXPECT_EQ(Runtime::newCapacity(20, 20), 30); // if factor of 1.5 is insufficient, grow exactly to minimum capacity EXPECT_EQ(Runtime::newCapacity(20, 40), 40); EXPECT_EQ(Runtime::newCapacity(20, 70), 70); // capacity has ceiling of SmallInt::kMaxValue EXPECT_EQ(Runtime::newCapacity(SmallInt::kMaxValue - 1, SmallInt::kMaxValue), SmallInt::kMaxValue); } TEST_F(RuntimeTest, InternLargeStr) { HandleScope scope(thread_); // Creating an ordinary large string should not affect on the intern table. Str str1(&scope, runtime_->newStrFromCStr("hello, world")); ASSERT_TRUE(str1.isLargeStr()); EXPECT_FALSE(runtime_->isInternedStr(thread_, str1)); // Interning the string should add it to the intern table and increase the // size of the intern table by one. Object sym1(&scope, Runtime::internStr(thread_, str1)); EXPECT_EQ(*sym1, *str1); EXPECT_TRUE(runtime_->isInternedStr(thread_, str1)); Str str2(&scope, runtime_->newStrFromCStr("goodbye, world")); ASSERT_TRUE(str2.isLargeStr()); EXPECT_NE(*str1, *str2); // Intern another string and make sure we get it back (as opposed to the // previously interned string). Object sym2(&scope, Runtime::internStr(thread_, str2)); EXPECT_EQ(*sym2, *str2); EXPECT_NE(*sym1, *sym2); // Create a unique copy of a previously created string. Str str3(&scope, runtime_->newStrFromCStr("hello, world")); ASSERT_TRUE(str3.isLargeStr()); EXPECT_NE(*str1, *str3); EXPECT_FALSE(runtime_->isInternedStr(thread_, str3)); // Interning a duplicate string should not affecct the intern table. Object sym3(&scope, Runtime::internStr(thread_, str3)); EXPECT_NE(*sym3, *str3); EXPECT_EQ(*sym3, *sym1); } TEST_F(RuntimeTest, InternSmallStr) { HandleScope scope(thread_); // Creating a small string should not affect the intern table. Str str(&scope, runtime_->newStrFromCStr("a")); ASSERT_TRUE(str.isSmallStr()); // Interning a small string should have no affect on the intern table. Object sym(&scope, Runtime::internStr(thread_, str)); EXPECT_TRUE(sym.isSmallStr()); EXPECT_EQ(*sym, *str); EXPECT_TRUE(runtime_->isInternedStr(thread_, str)); } TEST_F(RuntimeTest, InternCStr) { HandleScope scope(thread_); Object sym(&scope, Runtime::internStrFromCStr(thread_, "hello, world")); EXPECT_TRUE(sym.isStr()); EXPECT_TRUE(runtime_->isInternedStr(thread_, sym)); } TEST_F(RuntimeTest, IsInternWithInternedStrReturnsTrue) { HandleScope scope(thread_); Object str(&scope, Runtime::internStrFromCStr(thread_, "hello world")); EXPECT_TRUE(runtime_->isInternedStr(thread_, str)); } TEST_F(RuntimeTest, IsInternWithStrReturnsFalse) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("hello world")); EXPECT_FALSE(runtime_->isInternedStr(thread_, str)); } TEST_F(RuntimeTest, CollectAttributes) { HandleScope scope(thread_); Str foo(&scope, runtime_->newStrFromCStr("foo")); Str bar(&scope, runtime_->newStrFromCStr("bar")); Str baz(&scope, runtime_->newStrFromCStr("baz")); Tuple names(&scope, runtime_->newTupleWith3(foo, bar, baz)); Object obj1(&scope, SmallInt::fromWord(100)); Object obj2(&scope, SmallInt::fromWord(200)); Object obj3(&scope, SmallInt::fromWord(300)); Object obj4(&scope, NoneType::object()); Tuple consts(&scope, runtime_->newTupleWith4(obj1, obj2, obj3, obj4)); Locals locals; locals.argcount = 1; // Bytecode for the snippet: // // def __init__(self): // self.foo = 100 // self.foo = 200 // // The assignment to self.foo is intentionally duplicated to ensure that we // only record a single attribute name. const byte bytecode0[] = {LOAD_CONST, 0, LOAD_FAST, 0, STORE_ATTR, 0, LOAD_CONST, 1, LOAD_FAST, 0, STORE_ATTR, 0, RETURN_VALUE, 0}; Code code0(&scope, newCodeWithBytesConstsNamesLocals(bytecode0, consts, names, &locals)); Dict attributes(&scope, runtime_->newDict()); runtime_->collectAttributes(code0, attributes); // We should have collected a single attribute: 'foo' EXPECT_EQ(attributes.numItems(), 1); // Check that we collected 'foo' Object result(&scope, dictAtByStr(thread_, attributes, foo)); ASSERT_TRUE(result.isStr()); EXPECT_TRUE(Str::cast(*result).equals(*foo)); // Bytecode for the snippet: // // def __init__(self): // self.bar = 200 // self.baz = 300 const byte bytecode1[] = {LOAD_CONST, 1, LOAD_FAST, 0, STORE_ATTR, 1, LOAD_CONST, 2, LOAD_FAST, 0, STORE_ATTR, 2, RETURN_VALUE, 0}; Code code1(&scope, newCodeWithBytesConstsNamesLocals(bytecode1, consts, names, &locals)); runtime_->collectAttributes(code1, attributes); // We should have collected a two more attributes: 'bar' and 'baz' EXPECT_EQ(attributes.numItems(), 3); // Check that we collected 'bar' result = dictAtByStr(thread_, attributes, bar); ASSERT_TRUE(result.isStr()); EXPECT_TRUE(Str::cast(*result).equals(*bar)); // Check that we collected 'baz' result = dictAtByStr(thread_, attributes, baz); ASSERT_TRUE(result.isStr()); EXPECT_TRUE(Str::cast(*result).equals(*baz)); } TEST_F(RuntimeTest, CollectAttributesWithExtendedArg) { HandleScope scope(thread_); Str foo(&scope, runtime_->newStrFromCStr("foo")); Str bar(&scope, runtime_->newStrFromCStr("bar")); Tuple names(&scope, runtime_->newTupleWith2(foo, bar)); Object obj(&scope, NoneType::object()); Tuple consts(&scope, runtime_->newTupleWith1(obj)); Locals locals; locals.argcount = 1; locals.varcount = 300; // Bytecode for the snippet: // // def __init__(self): // self.foo = None // // There is an additional LOAD_FAST that is preceded by an EXTENDED_ARG // that must be skipped. const byte bytecode[] = {LOAD_CONST, 0, EXTENDED_ARG, 1, LOAD_FAST, 0, STORE_ATTR, 1, LOAD_CONST, 0, LOAD_FAST, 0, STORE_ATTR, 0, RETURN_VALUE, 0}; Code code(&scope, newCodeWithBytesConstsNamesLocals(bytecode, consts, names, &locals)); Dict attributes(&scope, runtime_->newDict()); runtime_->collectAttributes(code, attributes); // We should have collected a single attribute: 'foo' EXPECT_EQ(attributes.numItems(), 1); // Check that we collected 'foo' Object result(&scope, dictAtByStr(thread_, attributes, foo)); ASSERT_TRUE(result.isStr()); EXPECT_TRUE(Str::cast(*result).equals(*foo)); } TEST_F(RuntimeTest, GetTypeConstructor) { HandleScope scope(thread_); Type type(&scope, runtime_->newType()); EXPECT_TRUE(runtime_->classConstructor(type).isErrorNotFound()); Object func(&scope, makeTestFunction()); typeAtPutById(thread_, type, ID(__init__), func); EXPECT_EQ(runtime_->classConstructor(type), *func); } TEST_F(RuntimeTest, NewInstanceEmptyClass) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, "class MyEmptyClass: pass").isError()); Type type(&scope, mainModuleAt(runtime_, "MyEmptyClass")); Layout layout(&scope, type.instanceLayout()); EXPECT_EQ(layout.instanceSize(), 1 * kPointerSize); Type cls(&scope, layout.describedType()); EXPECT_TRUE(isStrEqualsCStr(cls.name(), "MyEmptyClass")); Instance instance(&scope, runtime_->newInstance(layout)); EXPECT_TRUE(instance.isInstance()); EXPECT_EQ(instance.header().layoutId(), layout.id()); } TEST_F(RuntimeTest, NewInstanceManyAttributes) { HandleScope scope(thread_); const char* src = R"( class MyTypeWithAttributes(): def __init__(self): self.a = 1 self.b = 2 self.c = 3 )"; ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Type type(&scope, mainModuleAt(runtime_, "MyTypeWithAttributes")); Layout layout(&scope, type.instanceLayout()); ASSERT_EQ(layout.instanceSize(), 4 * kPointerSize); Type cls(&scope, layout.describedType()); EXPECT_TRUE(isStrEqualsCStr(cls.name(), "MyTypeWithAttributes")); Instance instance(&scope, runtime_->newInstance(layout)); EXPECT_TRUE(instance.isInstance()); EXPECT_EQ(instance.header().layoutId(), layout.id()); } TEST_F(RuntimeTest, VerifySymbols) { HandleScope scope(thread_); Symbols* symbols = runtime_->symbols(); Str value(&scope, Str::empty()); for (int i = 0; i < static_cast<int>(SymbolId::kMaxId); i++) { SymbolId id = static_cast<SymbolId>(i); value = symbols->at(id); const char* expected = Symbols::predefinedSymbolAt(id); EXPECT_TRUE(runtime_->isInternedStr(thread_, value)) << "at symbol " << expected; EXPECT_TRUE(Str::cast(*value).equalsCStr(expected)) << "Incorrect symbol value for " << expected; } } static RawStr className(Runtime* runtime, RawObject o) { auto cls = Type::cast(runtime->typeOf(o)); auto name = Str::cast(cls.name()); return name; } TEST_F(RuntimeTest, TypeIds) { EXPECT_TRUE(isStrEqualsCStr(className(runtime_, Bool::trueObj()), "bool")); EXPECT_TRUE( isStrEqualsCStr(className(runtime_, NoneType::object()), "NoneType")); EXPECT_TRUE(isStrEqualsCStr( className(runtime_, runtime_->newStrFromCStr("abc")), "str")); for (word i = 0; i < 16; i++) { EXPECT_TRUE( isStrEqualsCStr(className(runtime_, SmallInt::fromWord(i)), "int")) << i; } } TEST_F(RuntimeTest, CallRunTwice) { ASSERT_FALSE(runFromCStr(runtime_, "x = 42").isError()); ASSERT_FALSE(runFromCStr(runtime_, "y = 1764").isError()); HandleScope scope(thread_); Object x(&scope, mainModuleAt(runtime_, "x")); EXPECT_TRUE(isIntEqualsWord(*x, 42)); Object y(&scope, mainModuleAt(runtime_, "y")); EXPECT_TRUE(isIntEqualsWord(*y, 1764)); } TEST_F(RuntimeStrTest, StrConcat) { HandleScope scope(thread_); Str str1(&scope, runtime_->newStrFromCStr("abc")); Str str2(&scope, runtime_->newStrFromCStr("def")); // Large strings. Str str3(&scope, runtime_->newStrFromCStr("0123456789abcdef")); Str str4(&scope, runtime_->newStrFromCStr("fedbca9876543210")); Object concat12(&scope, runtime_->strConcat(thread_, str1, str2)); Object concat34(&scope, runtime_->strConcat(thread_, str3, str4)); Object concat13(&scope, runtime_->strConcat(thread_, str1, str3)); Object concat31(&scope, runtime_->strConcat(thread_, str3, str1)); // Test that we don't make large strings when small srings would suffice. EXPECT_TRUE(isStrEqualsCStr(*concat12, "abcdef")); EXPECT_TRUE(isStrEqualsCStr(*concat34, "0123456789abcdeffedbca9876543210")); EXPECT_TRUE(isStrEqualsCStr(*concat13, "abc0123456789abcdef")); EXPECT_TRUE(isStrEqualsCStr(*concat31, "0123456789abcdefabc")); EXPECT_TRUE(concat12.isSmallStr()); EXPECT_TRUE(concat34.isLargeStr()); EXPECT_TRUE(concat13.isLargeStr()); EXPECT_TRUE(concat31.isLargeStr()); } TEST_F(RuntimeTypeCallTest, TypeCallNoInitMethod) { HandleScope scope(thread_); const char* src = R"( class MyTypeWithNoInitMethod(): def m(self): pass c = MyTypeWithNoInitMethod() )"; ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Object instance(&scope, mainModuleAt(runtime_, "c")); ASSERT_TRUE(instance.isInstance()); LayoutId layout_id = instance.layoutId(); Layout layout(&scope, runtime_->layoutAt(layout_id)); EXPECT_EQ(layout.instanceSize(), 1 * kPointerSize); Type cls(&scope, layout.describedType()); EXPECT_TRUE(isStrEqualsCStr(cls.name(), "MyTypeWithNoInitMethod")); } TEST_F(RuntimeTypeCallTest, TypeCallEmptyInitMethod) { HandleScope scope(thread_); const char* src = R"( class MyTypeWithEmptyInitMethod(): def __init__(self): pass def m(self): pass c = MyTypeWithEmptyInitMethod() )"; ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Object instance(&scope, mainModuleAt(runtime_, "c")); ASSERT_TRUE(instance.isInstance()); LayoutId layout_id = instance.layoutId(); Layout layout(&scope, runtime_->layoutAt(layout_id)); EXPECT_EQ(layout.instanceSize(), 1 * kPointerSize); Type cls(&scope, layout.describedType()); EXPECT_TRUE(isStrEqualsCStr(cls.name(), "MyTypeWithEmptyInitMethod")); } TEST_F(RuntimeTypeCallTest, TypeCallWithArguments) { HandleScope scope(thread_); const char* src = R"( class MyTypeWithAttributes(): def __init__(self, x): self.x = x def m(self): pass c = MyTypeWithAttributes(1) )"; ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Type type(&scope, mainModuleAt(runtime_, "MyTypeWithAttributes")); Object instance(&scope, mainModuleAt(runtime_, "c")); ASSERT_TRUE(instance.isInstance()); LayoutId layout_id = instance.layoutId(); // Since this class has extra attributes, its layout id should be greater than // the layout id from the type. ASSERT_GT(layout_id, Layout::cast(type.instanceLayout()).id()); Layout layout(&scope, runtime_->layoutAt(layout_id)); ASSERT_EQ(layout.instanceSize(), 2 * kPointerSize); Type cls(&scope, layout.describedType()); EXPECT_TRUE(isStrEqualsCStr(cls.name(), "MyTypeWithAttributes")); Object name(&scope, Runtime::internStrFromCStr(thread_, "x")); Object value(&scope, runtime_->attributeAt(thread_, instance, name)); EXPECT_FALSE(value.isError()); EXPECT_EQ(*value, SmallInt::fromWord(1)); } TEST_F(RuntimeTest, IsInstanceOf) { HandleScope scope(thread_); EXPECT_FALSE(runtime_->isInstanceOfInt(NoneType::object())); Object i(&scope, runtime_->newInt(123)); EXPECT_TRUE(i.isInt()); EXPECT_FALSE(runtime_->isInstanceOfStr(*i)); Object str(&scope, runtime_->newStrFromCStr("this is a long string")); EXPECT_TRUE(runtime_->isInstanceOfStr(*str)); EXPECT_FALSE(str.isInt()); ASSERT_FALSE(runFromCStr(runtime_, R"( class StopIterationSub(StopIteration): pass stop_iteration = StopIterationSub() )") .isError()); Object stop_iteration(&scope, mainModuleAt(runtime_, "stop_iteration")); EXPECT_TRUE(runtime_->isInstanceOfStopIteration(*stop_iteration)); EXPECT_TRUE(runtime_->isInstanceOfBaseException(*stop_iteration)); EXPECT_FALSE(runtime_->isInstanceOfSystemExit(*stop_iteration)); } TEST_F(RuntimeTest, IsInstanceOfBaseException) { ASSERT_FALSE(runFromCStr(runtime_, R"( class M(type): pass class C(metaclass=M): pass class D(Exception, metaclass=M): pass class E(Exception): pass c = C() d = D() e = E() )") .isError()); EXPECT_FALSE( runtime_->isInstanceOfBaseException(mainModuleAt(runtime_, "c"))); EXPECT_TRUE(runtime_->isInstanceOfBaseException(mainModuleAt(runtime_, "d"))); EXPECT_TRUE(runtime_->isInstanceOfBaseException(mainModuleAt(runtime_, "e"))); } TEST_F(RuntimeTest, IsInstanceOfSetBase) { ASSERT_FALSE(runFromCStr(runtime_, R"( class M(type): pass class C(metaclass=M): pass class D(set, metaclass=M): pass class E(set): pass class F(frozenset): pass c = C() d = D() e = E() f = F() )") .isError()); EXPECT_FALSE(runtime_->isInstanceOfSetBase(mainModuleAt(runtime_, "c"))); EXPECT_TRUE(runtime_->isInstanceOfSetBase(mainModuleAt(runtime_, "d"))); EXPECT_TRUE(runtime_->isInstanceOfSetBase(mainModuleAt(runtime_, "e"))); EXPECT_TRUE(runtime_->isInstanceOfSetBase(mainModuleAt(runtime_, "f"))); } TEST_F(RuntimeTest, IsInstanceOfUserBaseAcceptsMetaclassInstances) { HandleScope scope(thread_); EXPECT_FALSE(runFromCStr(runtime_, R"( class M(type): pass class IS(int, metaclass=M): pass i = IS() )") .isError()); Object i(&scope, mainModuleAt(runtime_, "i")); EXPECT_TRUE(runtime_->isInstanceOfUserIntBase(*i)); EXPECT_FALSE(runtime_->isInstanceOfUserStrBase(*i)); } TEST_F(RuntimeSetTest, EmptySetInvariants) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); EXPECT_EQ(set.numItems(), 0); ASSERT_TRUE(set.isSet()); ASSERT_TRUE(set.data().isTuple()); EXPECT_EQ(Tuple::cast(set.data()).length(), 0); } TEST_F(RuntimeSetTest, Add) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); Object value(&scope, SmallInt::fromWord(12345)); word hash = intHash(*value); // Store a value setAdd(thread_, set, value, hash); EXPECT_EQ(set.numItems(), 1); // Retrieve the stored value ASSERT_TRUE(setIncludes(thread_, set, value)); // Add a new value Object new_value(&scope, SmallInt::fromWord(5555)); word new_value_hash = intHash(*new_value); setAdd(thread_, set, new_value, new_value_hash); EXPECT_EQ(set.numItems(), 2); // Get the new value ASSERT_TRUE(setIncludes(thread_, set, new_value)); // Add a existing value Object same_value(&scope, SmallInt::fromWord(12345)); word same_value_hash = intHash(*same_value); RawObject old_value = setAdd(thread_, set, same_value, same_value_hash); EXPECT_EQ(set.numItems(), 2); EXPECT_EQ(old_value, *value); } TEST_F(RuntimeSetTest, Remove) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); Object value(&scope, SmallInt::fromWord(12345)); word hash = intHash(*value); // Removing a key that doesn't exist should fail EXPECT_FALSE(setRemove(thread_, set, value, hash)); setHashAndAdd(thread_, set, value); EXPECT_EQ(set.numItems(), 1); ASSERT_TRUE(setRemove(thread_, set, value, hash)); EXPECT_EQ(set.numItems(), 0); // Looking up a key that was deleted should fail ASSERT_FALSE(setIncludes(thread_, set, value)); } static RawObject makeKey(Runtime* runtime, int i) { byte text[]{"0123456789abcdeghiklmn"}; return runtime->newStrWithAll(View<byte>(text + i % 10, 10)); } TEST_F(RuntimeSetTest, Grow) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); // Fill up the dict - we insert an initial key to force the allocation of the // backing Tuple. Object init_key(&scope, SmallInt::fromWord(0)); setHashAndAdd(thread_, set, init_key); ASSERT_TRUE(set.data().isTuple()); word init_data_size = Tuple::cast(set.data()).length(); // Fill in one fewer keys than would require growing the underlying object // array again word num_keys = Runtime::kInitialSetCapacity; for (int i = 1; i < num_keys; i++) { Object key(&scope, makeKey(runtime_, i)); setHashAndAdd(thread_, set, key); } // Add another key which should force us to double the capacity Object straw(&scope, makeKey(runtime_, num_keys)); setHashAndAdd(thread_, set, straw); ASSERT_TRUE(set.data().isTuple()); word new_data_size = Tuple::cast(set.data()).length(); EXPECT_EQ(new_data_size, Runtime::kSetGrowthFactor * init_data_size); // Make sure we can still read all the stored keys for (int i = 1; i <= num_keys; i++) { Object key(&scope, makeKey(runtime_, i)); bool found = setIncludes(thread_, set, key); ASSERT_TRUE(found); } } TEST_F(RuntimeSetTest, UpdateSet) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); Set set1(&scope, runtime_->newSet()); Object set1_handle(&scope, *set1); for (word i = 0; i < 8; i++) { Object value(&scope, SmallInt::fromWord(i)); setHashAndAdd(thread_, set, value); } ASSERT_FALSE(setUpdate(thread_, set, set1_handle).isError()); ASSERT_EQ(set.numItems(), 8); for (word i = 4; i < 12; i++) { Object value(&scope, SmallInt::fromWord(i)); setHashAndAdd(thread_, set1, value); } ASSERT_FALSE(setUpdate(thread_, set, set1_handle).isError()); ASSERT_EQ(set.numItems(), 12); ASSERT_FALSE(setUpdate(thread_, set, set1_handle).isError()); ASSERT_EQ(set.numItems(), 12); } TEST_F(RuntimeSetTest, UpdateList) { HandleScope scope(thread_); List list(&scope, runtime_->newList()); Set set(&scope, runtime_->newSet()); for (word i = 0; i < 8; i++) { Object value(&scope, SmallInt::fromWord(i)); runtime_->listAdd(thread_, list, value); } for (word i = 4; i < 12; i++) { Object value(&scope, SmallInt::fromWord(i)); setHashAndAdd(thread_, set, value); } ASSERT_EQ(set.numItems(), 8); Object list_handle(&scope, *list); ASSERT_FALSE(setUpdate(thread_, set, list_handle).isError()); ASSERT_EQ(set.numItems(), 12); ASSERT_FALSE(setUpdate(thread_, set, list_handle).isError()); ASSERT_EQ(set.numItems(), 12); } TEST_F(RuntimeSetTest, UpdateListIterator) { HandleScope scope(thread_); List list(&scope, runtime_->newList()); Set set(&scope, runtime_->newSet()); for (word i = 0; i < 8; i++) { Object value(&scope, SmallInt::fromWord(i)); runtime_->listAdd(thread_, list, value); } for (word i = 4; i < 12; i++) { Object value(&scope, SmallInt::fromWord(i)); setHashAndAdd(thread_, set, value); } ASSERT_EQ(set.numItems(), 8); Object list_handle(&scope, *list); Object list_iterator(&scope, runtime_->newListIterator(list_handle)); ASSERT_FALSE(setUpdate(thread_, set, list_iterator).isError()); ASSERT_EQ(set.numItems(), 12); } TEST_F(RuntimeSetTest, UpdateTuple) { HandleScope scope(thread_); MutableTuple object_array(&scope, runtime_->newMutableTuple(8)); Set set(&scope, runtime_->newSet()); for (word i = 0; i < 8; i++) { object_array.atPut(i, SmallInt::fromWord(i)); } for (word i = 4; i < 12; i++) { Object value(&scope, SmallInt::fromWord(i)); setHashAndAdd(thread_, set, value); } ASSERT_EQ(set.numItems(), 8); Object object_array_handle(&scope, object_array.becomeImmutable()); ASSERT_FALSE(setUpdate(thread_, set, object_array_handle).isError()); ASSERT_EQ(set.numItems(), 12); } TEST_F(RuntimeSetTest, UpdateIterator) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); Int one(&scope, SmallInt::fromWord(1)); Int four(&scope, SmallInt::fromWord(4)); Object iterable(&scope, runtime_->newRange(one, four, one)); ASSERT_FALSE(setUpdate(thread_, set, iterable).isError()); ASSERT_EQ(set.numItems(), 3); } TEST_F(RuntimeSetTest, UpdateWithNonIterable) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); Object non_iterable(&scope, NoneType::object()); Object result(&scope, setUpdate(thread_, set, non_iterable)); ASSERT_TRUE(result.isError()); } TEST_F(RuntimeSetTest, EmptySetItersectionReturnsEmptySet) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); Set set1(&scope, runtime_->newSet()); // set() & set() Object result(&scope, setIntersection(thread_, set, set1)); ASSERT_TRUE(result.isSet()); EXPECT_EQ(Set::cast(*result).numItems(), 0); } TEST_F(RuntimeSetTest, ItersectionWithEmptySetReturnsEmptySet) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); Set set1(&scope, runtime_->newSet()); for (word i = 0; i < 8; i++) { Object value(&scope, SmallInt::fromWord(i)); setHashAndAdd(thread_, set1, value); } // set() & {0, 1, 2, 3, 4, 5, 6, 7} Object result(&scope, setIntersection(thread_, set, set1)); ASSERT_TRUE(result.isSet()); EXPECT_EQ(Set::cast(*result).numItems(), 0); // {0, 1, 2, 3, 4, 5, 6, 7} & set() Object result1(&scope, setIntersection(thread_, set1, set)); ASSERT_TRUE(result1.isSet()); EXPECT_EQ(Set::cast(*result1).numItems(), 0); } TEST_F(RuntimeSetTest, IntersectionReturnsSetWithCommonElements) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); Set set1(&scope, runtime_->newSet()); Object key(&scope, NoneType::object()); for (word i = 0; i < 8; i++) { Object value(&scope, SmallInt::fromWord(i)); setHashAndAdd(thread_, set1, value); } for (word i = 0; i < 4; i++) { Object value(&scope, SmallInt::fromWord(i)); setHashAndAdd(thread_, set, value); } // {0, 1, 2, 3} & {0, 1, 2, 3, 4, 5, 6, 7} Set result(&scope, setIntersection(thread_, set, set1)); EXPECT_EQ(Set::cast(*result).numItems(), 4); key = SmallInt::fromWord(0); EXPECT_TRUE(setIncludes(thread_, result, key)); key = SmallInt::fromWord(1); EXPECT_TRUE(setIncludes(thread_, result, key)); key = SmallInt::fromWord(2); EXPECT_TRUE(setIncludes(thread_, result, key)); key = SmallInt::fromWord(3); EXPECT_TRUE(setIncludes(thread_, result, key)); // {0, 1, 2, 3, 4, 5, 6, 7} & {0, 1, 2, 3} Set result1(&scope, setIntersection(thread_, set, set1)); EXPECT_EQ(Set::cast(*result1).numItems(), 4); key = SmallInt::fromWord(0); EXPECT_TRUE(setIncludes(thread_, result1, key)); key = SmallInt::fromWord(1); EXPECT_TRUE(setIncludes(thread_, result1, key)); key = SmallInt::fromWord(2); EXPECT_TRUE(setIncludes(thread_, result1, key)); key = SmallInt::fromWord(3); EXPECT_TRUE(setIncludes(thread_, result1, key)); } TEST_F(RuntimeSetTest, IntersectIterator) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); Int one(&scope, SmallInt::fromWord(1)); Int four(&scope, SmallInt::fromWord(4)); Object iterable(&scope, runtime_->newRange(one, four, one)); Set result(&scope, setIntersection(thread_, set, iterable)); EXPECT_EQ(result.numItems(), 0); Object key(&scope, SmallInt::fromWord(1)); setHashAndAdd(thread_, set, key); key = SmallInt::fromWord(2); setHashAndAdd(thread_, set, key); Set result1(&scope, setIntersection(thread_, set, iterable)); EXPECT_EQ(result1.numItems(), 2); EXPECT_TRUE(setIncludes(thread_, result1, key)); key = SmallInt::fromWord(1); EXPECT_TRUE(setIncludes(thread_, result1, key)); } TEST_F(RuntimeSetTest, IntersectWithNonIterable) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); Object non_iterable(&scope, NoneType::object()); Object result(&scope, setIntersection(thread_, set, non_iterable)); ASSERT_TRUE(result.isError()); } // Attribute tests // Set an attribute defined in __init__ TEST_F(RuntimeAttributeTest, SetInstanceAttribute) { const char* src = R"( class Foo: def __init__(self): self.attr = 'testing 123' def test(x): result = [] Foo.__init__(x) result.append(x.attr) x.attr = '321 testing' result.append(x.attr) return result )"; ASSERT_FALSE(runFromCStr(runtime_, src).isError()); // Create the instance HandleScope scope(thread_); Type type(&scope, mainModuleAt(runtime_, "Foo")); Layout layout(&scope, type.instanceLayout()); Object instance(&scope, runtime_->newInstance(layout)); // Run __init__ then RMW the attribute Function test(&scope, mainModuleAt(runtime_, "test")); Object result(&scope, Interpreter::call1(thread_, test, instance)); EXPECT_PYLIST_EQ(result, {"testing 123", "321 testing"}); } TEST_F(RuntimeAttributeTest, AddOverflowAttributes) { const char* src = R"( class Foo: pass def test(x): result = [] x.foo = 100 x.bar = 200 x.baz = 'hello' result.append(x.foo) result.append(x.bar) result.append(x.baz) x.foo = 'aaa' x.bar = 'bbb' x.baz = 'ccc' result.append(x.foo) result.append(x.bar) result.append(x.baz) return result )"; ASSERT_FALSE(runFromCStr(runtime_, src).isError()); // Create an instance of Foo HandleScope scope(thread_); Type type(&scope, mainModuleAt(runtime_, "Foo")); Layout layout(&scope, type.instanceLayout()); Instance foo1(&scope, runtime_->newInstance(layout)); LayoutId original_layout_id = layout.id(); // Add overflow attributes that should force layout transitions Function test(&scope, mainModuleAt(runtime_, "test")); Object result0(&scope, Interpreter::call1(thread_, test, foo1)); EXPECT_PYLIST_EQ(result0, {100, 200, "hello", "aaa", "bbb", "ccc"}); EXPECT_NE(foo1.layoutId(), original_layout_id); // Add the same set of attributes to a new instance, should arrive at the // same layout Instance foo2(&scope, runtime_->newInstance(layout)); Object result1(&scope, Interpreter::call1(thread_, test, foo2)); EXPECT_PYLIST_EQ(result1, {100, 200, "hello", "aaa", "bbb", "ccc"}); } TEST_F(RuntimeAttributeTest, ManipulateMultipleAttributes) { const char* src = R"( class Foo: def __init__(self): self.foo = 'foo' self.bar = 'bar' self.baz = 'baz' def test(x): result = [] Foo.__init__(x) result.append(x.foo) result.append(x.bar) result.append(x.baz) x.foo = 'aaa' x.bar = 'bbb' x.baz = 'ccc' result.append(x.foo) result.append(x.bar) result.append(x.baz) return result )"; ASSERT_FALSE(runFromCStr(runtime_, src).isError()); // Create the instance HandleScope scope(thread_); Type type(&scope, mainModuleAt(runtime_, "Foo")); Layout layout(&scope, type.instanceLayout()); Object instance(&scope, runtime_->newInstance(layout)); // Run the test Function test(&scope, mainModuleAt(runtime_, "test")); Object result(&scope, Interpreter::call1(thread_, test, instance)); EXPECT_PYLIST_EQ(result, {"foo", "bar", "baz", "aaa", "bbb", "ccc"}); } TEST_F(RuntimeAttributeTest, FetchConditionalInstanceAttribute) { const char* src = R"( def false(): return False class Foo: def __init__(self): self.foo = 'foo' if false(): self.bar = 'bar' foo = Foo() print(foo.bar) )"; EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, src), LayoutId::kAttributeError, "'Foo' object has no attribute 'bar'")); } TEST_F(RuntimeAttributeTest, DunderNewOnInstance) { const char* src = R"( result = [] class Foo: def __new__(cls): result.append("New") return object.__new__(cls) def __init__(self): result.append("Init") Foo() )"; ASSERT_FALSE(runFromCStr(runtime_, src).isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_PYLIST_EQ(result, {"New", "Init"}); } TEST_F(RuntimeAttributeTest, NoInstanceDictReturnsClassAttribute) { HandleScope scope(thread_); Object immediate(&scope, SmallInt::fromWord(-1)); RawObject attr = runtime_->attributeAtById(thread_, immediate, ID(__neg__)); ASSERT_TRUE(attr.isBoundMethod()); } TEST_F(RuntimeAttributeTest, DeleteKnownAttribute) { ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: def __init__(self): self.foo = 'foo' self.bar = 'bar' def test(): foo = Foo() del foo.bar )") .isError()); HandleScope scope(thread_); Function test(&scope, mainModuleAt(runtime_, "test")); Tuple args(&scope, runtime_->emptyTuple()); Object result(&scope, callFunction(test, args)); EXPECT_EQ(*result, NoneType::object()); } TEST_F(RuntimeAttributeTest, DeleteDescriptor) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = None class DeleteDescriptor: def __delete__(self, instance): global result result = self, instance descr = DeleteDescriptor() class Foo: bar = descr foo = Foo() del foo.bar )") .isError()); HandleScope scope(thread_); Object data(&scope, mainModuleAt(runtime_, "result")); ASSERT_TRUE(data.isTuple()); Tuple result(&scope, *data); ASSERT_EQ(result.length(), 2); Object descr(&scope, mainModuleAt(runtime_, "descr")); EXPECT_EQ(result.at(0), *descr); Object foo(&scope, mainModuleAt(runtime_, "foo")); EXPECT_EQ(result.at(1), *foo); } TEST_F(RuntimeAttributeTest, DeleteUnknownAttribute) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( class Foo: pass foo = Foo() del foo.bar )"), LayoutId::kAttributeError, "'Foo' object has no attribute 'bar'")); } TEST_F(RuntimeAttributeTest, DeleteAttributeWithDunderDelattr) { HandleScope scope(thread_); const char* src = R"( result = None class Foo: def __delattr__(self, name): global result result = self, name foo = Foo() del foo.bar )"; ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Object data(&scope, mainModuleAt(runtime_, "result")); ASSERT_TRUE(data.isTuple()); Tuple result(&scope, *data); ASSERT_EQ(result.length(), 2); Object foo(&scope, mainModuleAt(runtime_, "foo")); EXPECT_EQ(result.at(0), *foo); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "bar")); } TEST_F(RuntimeAttributeTest, DeleteAttributeWithDunderDelattrOnSuperclass) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = None class Foo: def __delattr__(self, name): global result result = self, name class Bar(Foo): pass bar = Bar() del bar.baz )") .isError()); HandleScope scope(thread_); Object data(&scope, mainModuleAt(runtime_, "result")); ASSERT_TRUE(data.isTuple()); Tuple result(&scope, *data); ASSERT_EQ(result.length(), 2); Object bar(&scope, mainModuleAt(runtime_, "bar")); EXPECT_EQ(result.at(0), *bar); EXPECT_TRUE(isStrEqualsCStr(result.at(1), "baz")); } TEST_F(RuntimeClassAttrTest, DeleteKnownAttribute) { ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: foo = 'foo' bar = 'bar' def test(): del Foo.bar )") .isError()); HandleScope scope(thread_); Function test(&scope, mainModuleAt(runtime_, "test")); Tuple args(&scope, runtime_->emptyTuple()); Object result(&scope, callFunction(test, args)); EXPECT_EQ(*result, NoneType::object()); } TEST_F(RuntimeAttributeTest, DeleteDescriptorOnMetaclass) { HandleScope scope(thread_); const char* src = R"( args = None class DeleteDescriptor: def __delete__(self, instance): global args args = (self, instance) descr = DeleteDescriptor() class FooMeta(type): attr = descr class Foo(metaclass=FooMeta): pass del Foo.attr )"; ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Object data(&scope, mainModuleAt(runtime_, "args")); ASSERT_TRUE(data.isTuple()); Tuple args(&scope, *data); ASSERT_EQ(args.length(), 2); Object descr(&scope, mainModuleAt(runtime_, "descr")); EXPECT_EQ(args.at(0), *descr); Object foo(&scope, mainModuleAt(runtime_, "Foo")); EXPECT_EQ(args.at(1), *foo); } TEST_F(RuntimeAttributeTest, DeleteUnknownClassAttribute) { const char* src = R"( class Foo: pass del Foo.bar )"; EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, src), LayoutId::kAttributeError, "type object 'Foo' has no attribute 'bar'")); } TEST_F(RuntimeAttributeTest, DeleteClassAttributeWithDunderDelattrOnMetaclass) { HandleScope scope(thread_); const char* src = R"( args = None class FooMeta(type): def __delattr__(self, name): global args args = self, name class Foo(metaclass=FooMeta): pass del Foo.bar )"; ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Object data(&scope, mainModuleAt(runtime_, "args")); ASSERT_TRUE(data.isTuple()); Tuple args(&scope, *data); ASSERT_EQ(args.length(), 2); Object foo(&scope, mainModuleAt(runtime_, "Foo")); EXPECT_EQ(args.at(0), *foo); Object attr(&scope, Runtime::internStrFromCStr(thread_, "bar")); EXPECT_EQ(args.at(1), *attr); } TEST_F( RuntimeTest, DeleteClassAttributeWithUnimplementedCacheInvalidationTerminatesPyroWhenCacheIsEnabled) { EXPECT_FALSE(runFromCStr(runtime_, R"( class C: def __len__(self): return 4 del C.__len__ )") .isError()); ASSERT_DEATH(static_cast<void>(runFromCStr(runtime_, R"( class C: def __setattr__(self, other): return 4 del C.__setattr__ )")), "unimplemented cache invalidation for type.__setattr__ update"); } TEST_F(RuntimeModuleAttrTest, DeleteUnknownAttribute) { HandleScope scope(thread_); const char* src = R"( def test(module): del module.foo )"; ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Function test(&scope, mainModuleAt(runtime_, "test")); Object obj(&scope, findMainModule(runtime_)); Tuple args(&scope, runtime_->newTupleWith1(obj)); EXPECT_TRUE(raised(callFunction(test, args), LayoutId::kAttributeError)); } TEST_F(RuntimeModuleAttrTest, DeleteKnownAttribute) { ASSERT_FALSE(runFromCStr(runtime_, R"( foo = 'testing 123' def test(module): del module.foo return 123 )") .isError()); HandleScope scope(thread_); Function test(&scope, mainModuleAt(runtime_, "test")); Object obj(&scope, findMainModule(runtime_)); Tuple args(&scope, runtime_->newTupleWith1(obj)); EXPECT_EQ(callFunction(test, args), SmallInt::fromWord(123)); Object attr(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object module(&scope, findMainModule(runtime_)); EXPECT_TRUE(runtime_->attributeAt(thread_, module, attr).isError()); } TEST_F(RuntimeIntTest, NewLargeIntWithDigits) { HandleScope scope(thread_); word negative_large_int = RawSmallInt::kMinValue - 1; uword digit = static_cast<uword>(negative_large_int); Int negative_largeint( &scope, runtime_->newLargeIntWithDigits(View<uword>(&digit, 1))); EXPECT_TRUE(isIntEqualsWord(*negative_largeint, negative_large_int)); word positive_large_int = RawSmallInt::kMaxValue + 1; digit = static_cast<uword>(positive_large_int); Int positive_largeint( &scope, runtime_->newLargeIntWithDigits(View<uword>(&digit, 1))); EXPECT_TRUE(isIntEqualsWord(*positive_largeint, positive_large_int)); } TEST_F(RuntimeIntTest, BinaryAndWithSmallInts) { HandleScope scope(thread_); Int left(&scope, SmallInt::fromWord(0xEA)); // 0b11101010 Int right(&scope, SmallInt::fromWord(0xDC)); // 0b11011100 Object result(&scope, runtime_->intBinaryAnd(thread_, left, right)); EXPECT_TRUE(isIntEqualsWord(*result, 0xC8)); // 0b11001000 } TEST_F(RuntimeIntTest, BinaryAndWithLargeInts) { HandleScope scope(thread_); // {0b00001111, 0b00110000, 0b00000001} const uword digits_left[] = {0x0F, 0x30, 0x1}; Int left(&scope, runtime_->newLargeIntWithDigits(digits_left)); // {0b00000011, 0b11110000, 0b00000010, 0b00000111} const uword digits_right[] = {0x03, 0xF0, 0x2, 0x7}; Int right(&scope, runtime_->newLargeIntWithDigits(digits_right)); Object result(&scope, runtime_->intBinaryAnd(thread_, left, right)); // {0b00000111, 0b01110000} const uword expected_digits[] = {0x03, 0x30}; EXPECT_TRUE(isIntEqualsDigits(*result, expected_digits)); Object result_commuted(&scope, runtime_->intBinaryAnd(thread_, right, left)); EXPECT_TRUE(isIntEqualsDigits(*result_commuted, expected_digits)); } TEST_F(RuntimeIntTest, BinaryAndWithNegativeLargeInts) { HandleScope scope(thread_); Int left(&scope, SmallInt::fromWord(-42)); // 0b11010110 const uword digits[] = {static_cast<uword>(-1), 0xF0, 0x2, 0x7}; Int right(&scope, runtime_->newLargeIntWithDigits(digits)); Object result(&scope, runtime_->intBinaryAnd(thread_, left, right)); const uword expected_digits[] = {static_cast<uword>(-42), 0xF0, 0x2, 0x7}; EXPECT_TRUE(isIntEqualsDigits(*result, expected_digits)); } TEST_F(RuntimeIntTest, BinaryOrWithSmallInts) { HandleScope scope(thread_); Int left(&scope, SmallInt::fromWord(0xAA)); // 0b10101010 Int right(&scope, SmallInt::fromWord(0x9C)); // 0b10011100 Object result(&scope, runtime_->intBinaryOr(thread_, left, right)); EXPECT_TRUE(isIntEqualsWord(*result, 0xBE)); // 0b10111110 } TEST_F(RuntimeIntTest, BinaryOrWithLargeInts) { HandleScope scope(thread_); // {0b00001100, 0b00110000, 0b00000001} const uword digits_left[] = {0x0C, 0x30, 0x1}; Int left(&scope, runtime_->newLargeIntWithDigits(digits_left)); // {0b00000011, 0b11010000, 0b00000010, 0b00000111} const uword digits_right[] = {0x03, 0xD0, 0x2, 0x7}; Int right(&scope, runtime_->newLargeIntWithDigits(digits_right)); Object result(&scope, runtime_->intBinaryOr(thread_, left, right)); // {0b00001111, 0b11110000, 0b00000011, 0b00000111} const uword expected_digits[] = {0x0F, 0xF0, 0x3, 0x7}; EXPECT_TRUE(isIntEqualsDigits(*result, expected_digits)); Object result_commuted(&scope, runtime_->intBinaryOr(thread_, right, left)); EXPECT_TRUE(isIntEqualsDigits(*result_commuted, expected_digits)); } TEST_F(RuntimeIntTest, BinaryOrWithNegativeLargeInts) { HandleScope scope(thread_); Int left(&scope, SmallInt::fromWord(-42)); // 0b11010110 const uword digits[] = {static_cast<uword>(-4), 0xF0, 0x2, static_cast<uword>(-1)}; Int right(&scope, runtime_->newLargeIntWithDigits(digits)); Object result(&scope, runtime_->intBinaryOr(thread_, left, right)); EXPECT_TRUE(isIntEqualsWord(*result, -2)); } TEST_F(RuntimeIntTest, BinaryXorWithSmallInts) { HandleScope scope(thread_); Int left(&scope, SmallInt::fromWord(0xAA)); // 0b10101010 Int right(&scope, SmallInt::fromWord(0x9C)); // 0b10011100 Object result(&scope, runtime_->intBinaryXor(thread_, left, right)); EXPECT_TRUE(isIntEqualsWord(*result, 0x36)); // 0b00110110 } TEST_F(RuntimeIntTest, BinaryXorWithLargeInts) { HandleScope scope(thread_); // {0b00001100, 0b00110000, 0b00000001} const uword digits_left[] = {0x0C, 0x30, 0x1}; Int left(&scope, runtime_->newLargeIntWithDigits(digits_left)); // {0b00000011, 0b11010000, 0b00000010, 0b00000111} const uword digits_right[] = {0x03, 0xD0, 0x2, 0x7}; Int right(&scope, runtime_->newLargeIntWithDigits(digits_right)); Object result(&scope, runtime_->intBinaryXor(thread_, left, right)); // {0b00001111, 0b11100000, 0b00000011, 0b00000111} const uword expected_digits[] = {0x0F, 0xE0, 0x3, 0x7}; EXPECT_TRUE(isIntEqualsDigits(*result, expected_digits)); Object result_commuted(&scope, runtime_->intBinaryXor(thread_, right, left)); EXPECT_TRUE(isIntEqualsDigits(*result_commuted, expected_digits)); } TEST_F(RuntimeIntTest, BinaryXorWithNegativeLargeInts) { HandleScope scope(thread_); Int left(&scope, SmallInt::fromWord(-42)); // 0b11010110 const uword digits[] = {static_cast<uword>(-1), 0xf0, 0x2, static_cast<uword>(-1)}; Int right(&scope, runtime_->newLargeIntWithDigits(digits)); Object result(&scope, runtime_->intBinaryXor(thread_, left, right)); const uword expected_digits[] = {0x29, ~uword{0xF0}, ~uword{0x2}, 0}; EXPECT_TRUE(isIntEqualsDigits(*result, expected_digits)); } TEST_F(RuntimeIntTest, NormalizeLargeIntToSmallInt) { HandleScope scope(thread_); const uword digits[] = {42}; LargeInt lint_42(&scope, newLargeIntWithDigits(digits)); Object norm_42(&scope, runtime_->normalizeLargeInt(thread_, lint_42)); EXPECT_TRUE(isIntEqualsWord(*norm_42, 42)); const uword digits2[] = {uword(-1)}; LargeInt lint_neg1(&scope, newLargeIntWithDigits(digits2)); Object norm_neg1(&scope, runtime_->normalizeLargeInt(thread_, lint_neg1)); EXPECT_TRUE(isIntEqualsWord(*norm_neg1, -1)); const uword digits3[] = {uword(RawSmallInt::kMinValue)}; LargeInt lint_min(&scope, newLargeIntWithDigits(digits3)); Object norm_min(&scope, runtime_->normalizeLargeInt(thread_, lint_min)); EXPECT_TRUE(isIntEqualsWord(*norm_min, RawSmallInt::kMinValue)); const uword digits4[] = {RawSmallInt::kMaxValue}; LargeInt lint_max(&scope, newLargeIntWithDigits(digits4)); Object norm_max(&scope, runtime_->normalizeLargeInt(thread_, lint_max)); EXPECT_TRUE(isIntEqualsWord(*norm_max, RawSmallInt::kMaxValue)); const uword digits5[] = {uword(-4), kMaxUword}; LargeInt lint_sext_neg_4(&scope, newLargeIntWithDigits(digits5)); Object norm_neg_4(&scope, runtime_->normalizeLargeInt(thread_, lint_sext_neg_4)); EXPECT_TRUE(isIntEqualsWord(*norm_neg_4, -4)); const uword digits6[] = {uword(-13), kMaxUword, kMaxUword, kMaxUword}; LargeInt lint_sext_neg_13(&scope, newLargeIntWithDigits(digits6)); Object norm_neg_13(&scope, runtime_->normalizeLargeInt(thread_, lint_sext_neg_13)); EXPECT_TRUE(isIntEqualsWord(*norm_neg_13, -13)); const uword digits7[] = {66, 0}; LargeInt lint_zext_66(&scope, newLargeIntWithDigits(digits7)); Object norm_66(&scope, runtime_->normalizeLargeInt(thread_, lint_zext_66)); EXPECT_TRUE(isIntEqualsWord(*norm_66, 66)); } TEST_F(RuntimeIntTest, NormalizeLargeIntToLargeInt) { HandleScope scope(thread_); const uword digits[] = {kMaxWord}; LargeInt lint_max(&scope, newLargeIntWithDigits(digits)); Object norm_max(&scope, runtime_->normalizeLargeInt(thread_, lint_max)); EXPECT_TRUE(isIntEqualsWord(*norm_max, kMaxWord)); const uword digits2[] = {uword(kMinWord)}; LargeInt lint_min(&scope, newLargeIntWithDigits(digits2)); Object norm_min(&scope, runtime_->normalizeLargeInt(thread_, lint_min)); EXPECT_TRUE(isIntEqualsWord(*norm_min, kMinWord)); const uword digits3[] = {kMaxWord - 7, 0, 0}; LargeInt lint_max_sub_7_zext(&scope, newLargeIntWithDigits(digits3)); Object norm_max_sub_7( &scope, runtime_->normalizeLargeInt(thread_, lint_max_sub_7_zext)); EXPECT_TRUE(isIntEqualsWord(*norm_max_sub_7, kMaxWord - 7)); const uword digits4[] = {uword(kMinWord) + 9, kMaxUword}; LargeInt lint_min_plus_9_sext(&scope, newLargeIntWithDigits(digits4)); Object norm_min_plus_9( &scope, runtime_->normalizeLargeInt(thread_, lint_min_plus_9_sext)); EXPECT_TRUE(isIntEqualsWord(*norm_min_plus_9, kMinWord + 9)); const uword digits5[] = {0, kMaxUword}; LargeInt lint_no_sext(&scope, newLargeIntWithDigits(digits5)); Object norm_no_sext(&scope, runtime_->normalizeLargeInt(thread_, lint_no_sext)); const uword expected_digits1[] = {0, kMaxUword}; EXPECT_TRUE(isIntEqualsDigits(*norm_no_sext, expected_digits1)); const uword digits6[] = {kMaxUword, 0}; LargeInt lint_no_zext(&scope, newLargeIntWithDigits(digits6)); Object norm_no_zext(&scope, runtime_->normalizeLargeInt(thread_, lint_no_zext)); const uword expected_digits2[] = {kMaxUword, 0}; EXPECT_TRUE(isIntEqualsDigits(*norm_no_zext, expected_digits2)); } TEST_F(RuntimeTest, ClassWithTypeMetaclassIsConcreteType) { const char* src = R"( # This is equivalent to `class Foo(type)` class Foo(type, metaclass=type): pass class Bar(Foo): pass )"; HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Object foo(&scope, mainModuleAt(runtime_, "Foo")); EXPECT_TRUE(foo.isType()); Object bar(&scope, mainModuleAt(runtime_, "Bar")); EXPECT_TRUE(bar.isType()); } TEST_F(RuntimeTest, ClassWithCustomMetaclassIsntConcreteType) { const char* src = R"( class MyMeta(type): pass class Foo(type, metaclass=MyMeta): pass )"; HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Object foo(&scope, mainModuleAt(runtime_, "Foo")); EXPECT_FALSE(foo.isType()); } TEST_F(RuntimeTest, ClassWithTypeMetaclassIsInstanceOfType) { const char* src = R"( class Foo(type): pass class Bar(Foo): pass )"; HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Object foo(&scope, mainModuleAt(runtime_, "Foo")); EXPECT_TRUE(runtime_->isInstanceOfType(*foo)); Object bar(&scope, mainModuleAt(runtime_, "Bar")); EXPECT_TRUE(runtime_->isInstanceOfType(*bar)); } TEST_F(RuntimeTest, ClassWithCustomMetaclassIsInstanceOfType) { const char* src = R"( class MyMeta(type): pass class Foo(type, metaclass=MyMeta): pass )"; HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Object foo(&scope, mainModuleAt(runtime_, "Foo")); EXPECT_TRUE(runtime_->isInstanceOfType(*foo)); } TEST_F(RuntimeTest, VerifyMetaclassHierarchy) { const char* src = R"( class GrandMeta(type): pass class ParentMeta(type, metaclass=GrandMeta): pass class ChildMeta(type, metaclass=ParentMeta): pass )"; HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Object type(&scope, runtime_->typeAt(LayoutId::kType)); Object grand_meta(&scope, mainModuleAt(runtime_, "GrandMeta")); EXPECT_EQ(runtime_->typeOf(*grand_meta), *type); Object parent_meta(&scope, mainModuleAt(runtime_, "ParentMeta")); EXPECT_EQ(runtime_->typeOf(*parent_meta), *grand_meta); Object child_meta(&scope, mainModuleAt(runtime_, "ChildMeta")); EXPECT_EQ(runtime_->typeOf(*child_meta), *parent_meta); } TEST_F(RuntimeMetaclassTest, CallMetaclass) { const char* src = R"( class MyMeta(type): pass Foo = MyMeta('Foo', (), {}) )"; HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Object mymeta(&scope, mainModuleAt(runtime_, "MyMeta")); Object foo(&scope, mainModuleAt(runtime_, "Foo")); EXPECT_EQ(runtime_->typeOf(*foo), *mymeta); EXPECT_FALSE(foo.isType()); EXPECT_TRUE(runtime_->isInstanceOfType(*foo)); } TEST_F(RuntimeTest, SubclassBuiltinSubclass) { const char* src = R"( class Test(Exception): 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(), 4); EXPECT_EQ(mro.at(0), *type); EXPECT_EQ(mro.at(1), runtime_->typeAt(LayoutId::kException)); EXPECT_EQ(mro.at(2), runtime_->typeAt(LayoutId::kBaseException)); EXPECT_EQ(mro.at(3), runtime_->typeAt(LayoutId::kObject)); } TEST_F(RuntimeTest, GeneratorFrameCreate) { const char* src = R"( def gen(): yield 12 )"; HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, src).isError()); Object gen_obj(&scope, mainModuleAt(runtime_, "gen")); ASSERT_TRUE(gen_obj.isFunction()); Function gen(&scope, *gen_obj); Object frame_obj(&scope, runtime_->newGeneratorFrame(gen)); ASSERT_TRUE(frame_obj.isGeneratorFrame()); GeneratorFrame generator_frame(&scope, *frame_obj); EXPECT_EQ(generator_frame.maxStackSize(), SmallInt::cast(gen.stacksizeOrBuiltin()).value()); } TEST_F(RuntimeModuleTest, ImportModuleFromInitTab) { ASSERT_FALSE(runFromCStr(runtime_, "import _empty").isError()); HandleScope scope(thread_); Object mod(&scope, mainModuleAt(runtime_, "_empty")); EXPECT_TRUE(mod.isModule()); } TEST_F(RuntimeModuleTest, NewModuleSetsDictValuesAndModuleProxy) { HandleScope scope(thread_); // Create Module Object name(&scope, runtime_->newStrFromCStr("mymodule")); Module module(&scope, runtime_->newModule(name)); Object modules(&scope, runtime_->modules()); ASSERT_FALSE( objectSetItem(thread_, modules, name, module).isErrorException()); Str mod_name(&scope, moduleAtByCStr(runtime_, "mymodule", "__name__")); EXPECT_TRUE(mod_name.equalsCStr("mymodule")); EXPECT_EQ(moduleAtByCStr(runtime_, "mymodule", "__doc__"), NoneType::object()); EXPECT_EQ(moduleAtByCStr(runtime_, "mymodule", "__package__"), NoneType::object()); EXPECT_EQ(moduleAtByCStr(runtime_, "mymodule", "__loader__"), NoneType::object()); EXPECT_EQ(moduleAtByCStr(runtime_, "mymodule", "__spec__"), NoneType::object()); ModuleProxy module_proxy(&scope, module.moduleProxy()); EXPECT_EQ(module_proxy.module(), *module); } TEST_F(RuntimeFunctionAttrTest, SetAttribute) { ASSERT_FALSE(runFromCStr(runtime_, R"( def foo(): pass foo.x = 3 )") .isError()); HandleScope scope(thread_); Function function(&scope, mainModuleAt(runtime_, "foo")); Dict function_dict(&scope, function.dict()); Str name(&scope, runtime_->newStrFromCStr("x")); Object value(&scope, dictAtByStr(thread_, function_dict, name)); EXPECT_TRUE(isIntEqualsWord(*value, 3)); } TEST_F(RuntimeTest, NotMatchingCellAndVarNamesSetsCell2ArgToNone) { HandleScope scope(thread_); word argcount = 3; word kwargcount = 0; word nlocals = 3; Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar")); Object baz(&scope, Runtime::internStrFromCStr(thread_, "baz")); Tuple varnames(&scope, runtime_->newTupleWith3(foo, bar, baz)); Object foobar(&scope, Runtime::internStrFromCStr(thread_, "foobar")); Object foobaz(&scope, Runtime::internStrFromCStr(thread_, "foobaz")); Tuple cellvars(&scope, runtime_->newTupleWith2(foobar, foobaz)); Object code_code(&scope, Bytes::empty()); Tuple empty_tuple(&scope, runtime_->emptyTuple()); Object empty_bytes(&scope, Bytes::empty()); Object empty_str(&scope, Str::empty()); Code code(&scope, runtime_->newCode(argcount, /*posonlyargcount=*/0, kwargcount, nlocals, /*stacksize=*/0, /*flags=*/0, code_code, empty_tuple, empty_tuple, varnames, empty_tuple, cellvars, empty_str, empty_str, 0, empty_bytes)); EXPECT_TRUE(code.cell2arg().isNoneType()); } TEST_F(RuntimeTest, MatchingCellAndVarNamesCreatesCell2Arg) { HandleScope scope(thread_); word argcount = 3; word kwargcount = 0; word nlocals = 3; Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar")); Object baz(&scope, Runtime::internStrFromCStr(thread_, "baz")); Tuple varnames(&scope, runtime_->newTupleWith3(foo, bar, baz)); Object foobar(&scope, Runtime::internStrFromCStr(thread_, "foobar")); Tuple cellvars(&scope, runtime_->newTupleWith2(baz, foobar)); Object code_code(&scope, Bytes::empty()); Tuple empty_tuple(&scope, runtime_->emptyTuple()); Object empty_bytes(&scope, Bytes::empty()); Object empty_str(&scope, Str::empty()); Code code(&scope, runtime_->newCode(argcount, /*posonlyargcount=*/0, kwargcount, nlocals, /*stacksize=*/0, /*flags=*/0, code_code, empty_tuple, empty_tuple, varnames, empty_tuple, cellvars, empty_str, empty_str, 0, empty_bytes)); ASSERT_FALSE(code.cell2arg().isNoneType()); Tuple cell2arg(&scope, code.cell2arg()); ASSERT_EQ(cell2arg.length(), 2); Object cell2arg_value(&scope, cell2arg.at(0)); EXPECT_TRUE(isIntEqualsWord(*cell2arg_value, 2)); EXPECT_EQ(cell2arg.at(1), NoneType::object()); } TEST_F(RuntimeTest, NewCodeWithCellvarsTurnsOffNofreeFlag) { HandleScope scope(thread_); word argcount = 3; word nlocals = 3; Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo")); Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar")); Object baz(&scope, Runtime::internStrFromCStr(thread_, "baz")); Tuple varnames(&scope, runtime_->newTupleWith3(foo, bar, baz)); Object foobar(&scope, Runtime::internStrFromCStr(thread_, "foobar")); Tuple cellvars(&scope, runtime_->newTupleWith2(baz, foobar)); Object code_code(&scope, Bytes::empty()); Tuple empty_tuple(&scope, runtime_->emptyTuple()); Object empty_bytes(&scope, Bytes::empty()); Object empty_str(&scope, Str::empty()); Code code(&scope, runtime_->newCode( argcount, /*posonlyargcount=*/0, /*kwonlyargcount=*/0, nlocals, /*stacksize=*/0, /*flags=*/0, code_code, empty_tuple, empty_tuple, varnames, empty_tuple, cellvars, empty_str, empty_str, 0, empty_bytes)); EXPECT_FALSE(code.flags() & Code::Flags::kNofree); } TEST_F(RuntimeTest, NewCodeWithNoFreevarsOrCellvarsSetsNofreeFlag) { HandleScope scope(thread_); Object foobar(&scope, runtime_->newStrFromCStr("foobar")); Tuple varnames(&scope, runtime_->newTupleWith1(foobar)); Object code_code(&scope, Bytes::empty()); Tuple empty_tuple(&scope, runtime_->emptyTuple()); Object empty_bytes(&scope, Bytes::empty()); Object empty_str(&scope, Str::empty()); Object code_obj( &scope, runtime_->newCode( /*argcount=*/0, /*posonlyargcount=*/0, /*kwonlyargcount=*/0, /*nlocals=*/0, /*stacksize=*/0, /*flags=*/0, code_code, empty_tuple, empty_tuple, varnames, empty_tuple, empty_tuple, empty_str, empty_str, 0, empty_bytes)); ASSERT_TRUE(code_obj.isCode()); Code code(&scope, *code_obj); EXPECT_TRUE(code.flags() & Code::Flags::kNofree); } TEST_F(RuntimeTest, NewCodeWithArgcountGreaterThanVarnamesLengthRaisesValueError) { HandleScope scope(thread_); Tuple varnames(&scope, newTupleWithNone(1)); Tuple cellvars(&scope, newTupleWithNone(2)); Object code_code(&scope, Bytes::empty()); Tuple empty_tuple(&scope, runtime_->emptyTuple()); Object empty_bytes(&scope, Bytes::empty()); Object empty_str(&scope, Str::empty()); EXPECT_TRUE(raisedWithStr( runtime_->newCode(/*argcount=*/10, /*posonlyargcount=*/0, /*kwonlyargcount=*/0, /*nlocals=*/0, /*stacksize=*/0, /*flags=*/0, code_code, empty_tuple, empty_tuple, varnames, empty_tuple, cellvars, empty_str, empty_str, 0, empty_bytes), LayoutId::kValueError, "code: varnames is too small")); } TEST_F(RuntimeTest, NewCodeWithKwonlyargcountGreaterThanVarnamesLengthRaisesValueError) { HandleScope scope(thread_); Tuple varnames(&scope, newTupleWithNone(1)); Tuple cellvars(&scope, newTupleWithNone(2)); Object code_code(&scope, Bytes::empty()); Tuple empty_tuple(&scope, runtime_->emptyTuple()); Object empty_bytes(&scope, Bytes::empty()); Object empty_str(&scope, Str::empty()); EXPECT_TRUE(raisedWithStr( runtime_->newCode(/*argcount=*/0, /*posonlyargcount=*/0, /*kwonlyargcount=*/10, /*nlocals=*/0, /*stacksize=*/0, /*flags=*/0, code_code, empty_tuple, empty_tuple, varnames, empty_tuple, cellvars, empty_str, empty_str, 0, empty_bytes), LayoutId::kValueError, "code: varnames is too small")); } TEST_F(RuntimeTest, NewCodeWithTotalArgsGreaterThanVarnamesLengthRaisesValueError) { HandleScope scope(thread_); Tuple varnames(&scope, newTupleWithNone(1)); Tuple cellvars(&scope, newTupleWithNone(2)); Object code_code(&scope, Bytes::empty()); Tuple empty_tuple(&scope, runtime_->emptyTuple()); Object empty_bytes(&scope, Bytes::empty()); Object empty_str(&scope, Str::empty()); EXPECT_TRUE(raisedWithStr( runtime_->newCode(/*argcount=*/1, /*posonlyargcount=*/0, /*kwonlyargcount=*/1, /*nlocals=*/0, /*stacksize=*/0, /*flags=*/0, code_code, empty_tuple, empty_tuple, varnames, empty_tuple, cellvars, empty_str, empty_str, 0, empty_bytes), LayoutId::kValueError, "code: varnames is too small")); } TEST_F(RuntimeTest, NewWeakLink) { HandleScope scope(thread_); Object referent(&scope, runtime_->newList()); Object prev(&scope, runtime_->newInt(2)); Object next(&scope, runtime_->newInt(3)); WeakLink link(&scope, runtime_->newWeakLink(thread_, referent, prev, next)); EXPECT_EQ(link.referent(), *referent); EXPECT_EQ(link.prev(), *prev); EXPECT_EQ(link.next(), *next); } // Set is not special except that it is a builtin type with sealed attributes. TEST_F(RuntimeTest, SetHasSameSizeCreatedTwoDifferentWays) { HandleScope scope(thread_); Layout layout(&scope, runtime_->layoutAt(LayoutId::kSet)); Set set1(&scope, runtime_->newInstance(layout)); Set set2(&scope, runtime_->newSet()); EXPECT_EQ(set1.size(), set2.size()); } // Set is not special except that it is a builtin type with sealed attributes. TEST_F(RuntimeTest, SealedClassLayoutDoesNotHaveSpaceForOverflowAttributes) { HandleScope scope(thread_); Layout layout(&scope, runtime_->layoutAt(LayoutId::kSet)); EXPECT_TRUE(layout.isSealed()); word expected_set_size = kPointerSize * layout.numInObjectAttributes(); EXPECT_EQ(layout.instanceSize(), expected_set_size); } TEST_F(RuntimeTest, SettingNewAttributeOnSealedClassRaisesAttributeError) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); Str attr(&scope, runtime_->newStrFromCStr("attr")); Str value(&scope, runtime_->newStrFromCStr("value")); Object result(&scope, instanceSetAttr(thread_, set, attr, value)); EXPECT_TRUE(raised(*result, LayoutId::kAttributeError)); } TEST_F(RuntimeTest, InstanceAtPutWithReadOnlyAttributeRaisesAttributeError) { HandleScope scope(thread_); LayoutId layout_id = LayoutId::kUserWarning; Object previous_layout(&scope, runtime_->layoutAt(layout_id)); BuiltinAttribute attrs[] = { {ID(__globals__), 0, AttributeFlags::kReadOnly}, }; Type type(&scope, addBuiltinType(thread_, ID(UserWarning), layout_id, LayoutId::kObject, attrs, /*size=*/kPointerSize, /*basetype=*/true)); Layout layout(&scope, type.instanceLayout()); runtime_->layoutAtPut(layout_id, *layout); Instance instance(&scope, runtime_->newInstance(layout)); Str attribute_name(&scope, Runtime::internStrFromCStr(thread_, "__globals__")); Object value(&scope, NoneType::object()); EXPECT_TRUE( raisedWithStr(instanceSetAttr(thread_, instance, attribute_name, value), LayoutId::kAttributeError, "'UserWarning.__globals__' attribute is read-only")); runtime_->layoutAtPut(layout_id, *previous_layout); } // Exception attributes can be set on the fly. TEST_F(RuntimeTest, NonSealedClassHasSpaceForOverflowAttrbutes) { HandleScope scope(thread_); Layout layout(&scope, runtime_->layoutAt(LayoutId::kMemoryError)); EXPECT_TRUE(layout.hasTupleOverflow()); EXPECT_EQ(layout.instanceSize(), (layout.numInObjectAttributes() + 1) * kPointerSize); // 1=overflow } // User-defined class attributes can be set on the fly. TEST_F(RuntimeTest, UserCanSetOverflowAttributeOnUserDefinedClass) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C(): pass a = C() )") .isError()); Instance a(&scope, mainModuleAt(runtime_, "a")); Str attr(&scope, runtime_->newStrFromCStr("attr")); Str value(&scope, runtime_->newStrFromCStr("value")); Object result(&scope, instanceSetAttr(thread_, a, attr, value)); ASSERT_FALSE(result.isError()); EXPECT_EQ(instanceGetAttribute(thread_, a, attr), *value); } TEST_F(RuntimeTest, IsMappingReturnsFalseOnSet) { HandleScope scope(thread_); Set set(&scope, runtime_->newSet()); EXPECT_FALSE(runtime_->isMapping(thread_, set)); } TEST_F(RuntimeTest, IsMappingReturnsTrueOnDict) { HandleScope scope(thread_); Dict dict(&scope, runtime_->newDict()); EXPECT_TRUE(runtime_->isMapping(thread_, dict)); } TEST_F(RuntimeTest, IsMappingReturnsTrueOnList) { HandleScope scope(thread_); List list(&scope, runtime_->newList()); EXPECT_TRUE(runtime_->isMapping(thread_, list)); } TEST_F(RuntimeTest, IsMappingReturnsTrueOnCustomClassWithMethod) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C(): def __getitem__(self, key): pass o = C() )") .isError()); HandleScope scope(thread_); Object obj(&scope, mainModuleAt(runtime_, "o")); EXPECT_TRUE(runtime_->isMapping(thread_, obj)); } TEST_F(RuntimeTest, IsMappingWithClassAttrNotCallableReturnsTrue) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C(): __getitem__ = 4 o = C() )") .isError()); HandleScope scope(thread_); Object obj(&scope, mainModuleAt(runtime_, "o")); EXPECT_TRUE(runtime_->isMapping(thread_, obj)); } TEST_F(RuntimeTest, IsMappingReturnsFalseOnCustomClassWithoutMethod) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C(): pass o = C() )") .isError()); HandleScope scope(thread_); Object obj(&scope, mainModuleAt(runtime_, "o")); EXPECT_FALSE(runtime_->isMapping(thread_, obj)); } TEST_F(RuntimeTest, IsMappingWithInstanceAttrReturnsFalse) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C(): pass o = C() o.__getitem__ = 4 )") .isError()); HandleScope scope(thread_); Object obj(&scope, mainModuleAt(runtime_, "o")); EXPECT_FALSE(runtime_->isMapping(thread_, obj)); } TEST_F(RuntimeTest, ModuleBuiltinsExists) { ASSERT_FALSE(moduleAtByCStr(runtime_, "builtins", "__name__").isError()); } TEST_F(RuntimeTest, ObjectEqualsWithSameObjectReturnsTrue) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C(): def __eq__(self, other): return False i = C() )") .isError()); HandleScope scope(thread_); Object i(&scope, mainModuleAt(runtime_, "i")); EXPECT_EQ(Runtime::objectEquals(thread_, *i, *i), Bool::trueObj()); } TEST_F(RuntimeTest, ObjectEqualsWithBoolAndSmallInt) { EXPECT_EQ( Runtime::objectEquals(thread_, Bool::trueObj(), SmallInt::fromWord(1)), Bool::trueObj()); EXPECT_EQ( Runtime::objectEquals(thread_, Bool::trueObj(), SmallInt::fromWord(0)), Bool::falseObj()); EXPECT_EQ( Runtime::objectEquals(thread_, Bool::trueObj(), SmallInt::fromWord(100)), Bool::falseObj()); EXPECT_EQ( Runtime::objectEquals(thread_, Bool::falseObj(), SmallInt::fromWord(0)), Bool::trueObj()); EXPECT_EQ( Runtime::objectEquals(thread_, Bool::falseObj(), SmallInt::fromWord(1)), Bool::falseObj()); EXPECT_EQ( Runtime::objectEquals(thread_, Bool::falseObj(), SmallInt::fromWord(100)), Bool::falseObj()); } TEST_F(RuntimeTest, ObjectEqualsWithSmallIntAndBool) { EXPECT_EQ( Runtime::objectEquals(thread_, SmallInt::fromWord(1), Bool::trueObj()), Bool::trueObj()); EXPECT_EQ( Runtime::objectEquals(thread_, SmallInt::fromWord(0), Bool::trueObj()), Bool::falseObj()); EXPECT_EQ( Runtime::objectEquals(thread_, SmallInt::fromWord(100), Bool::trueObj()), Bool::falseObj()); EXPECT_EQ( Runtime::objectEquals(thread_, SmallInt::fromWord(0), Bool::falseObj()), Bool::trueObj()); EXPECT_EQ( Runtime::objectEquals(thread_, SmallInt::fromWord(1), Bool::falseObj()), Bool::falseObj()); EXPECT_EQ( Runtime::objectEquals(thread_, SmallInt::fromWord(100), Bool::falseObj()), Bool::falseObj()); } TEST_F(RuntimeTest, ObjectEqualsCallsDunderEq) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __eq__(self, other): return True i = C() )") .isError()); HandleScope scope(thread_); Object i(&scope, mainModuleAt(runtime_, "i")); EXPECT_EQ(Runtime::objectEquals(thread_, *i, NoneType::object()), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, *i, SmallStr::fromCStr("foo")), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, *i, Bool::falseObj()), Bool::trueObj()); } TEST_F(RuntimeTest, ObjectEqualsCallsStrSubclassDunderEq) { ASSERT_FALSE(runFromCStr(runtime_, R"( class StrSub(str): def __eq__(self, other): return True i = StrSub("foo") )") .isError()); HandleScope scope(thread_); Object i(&scope, mainModuleAt(runtime_, "i")); EXPECT_EQ(Runtime::objectEquals(thread_, Str::empty(), *i), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, *i, Str::empty()), Bool::trueObj()); LargeStr large_str(&scope, runtime_->newStrFromCStr("foobarbazbumbam")); EXPECT_EQ(Runtime::objectEquals(thread_, *large_str, *i), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, *i, *large_str), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, SmallInt::fromWord(0), *i), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, *i, SmallInt::fromWord(0)), Bool::trueObj()); } TEST_F(RuntimeTest, ObjectEqualsCallsIntSubclassDunderEq) { ASSERT_FALSE(runFromCStr(runtime_, R"( class IntSub(int): def __eq__(self, other): return True i = IntSub(7) )") .isError()); HandleScope scope(thread_); Object i(&scope, mainModuleAt(runtime_, "i")); EXPECT_EQ(Runtime::objectEquals(thread_, SmallInt::fromWord(1), *i), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, *i, SmallInt::fromWord(1)), Bool::trueObj()); const uword digits[] = {1, 2}; LargeInt large_int(&scope, runtime_->newLargeIntWithDigits(digits)); EXPECT_EQ(Runtime::objectEquals(thread_, *i, *large_int), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, *large_int, *i), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, *i, Bool::trueObj()), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, NoneType::object(), *i), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, *i, NoneType::object()), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, Bool::trueObj(), *i), Bool::falseObj()); } TEST_F(RuntimeTest, ObjectEqualsWithSmallStrReturnsBool) { RawSmallStr s0 = SmallStr::empty(); RawSmallStr s1 = SmallStr::fromCStr("foo"); EXPECT_EQ(Runtime::objectEquals(thread_, s0, s0), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, s0, s1), Bool::falseObj()); EXPECT_EQ(Runtime::objectEquals(thread_, s1, s0), Bool::falseObj()); EXPECT_EQ(Runtime::objectEquals(thread_, s1, s1), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, NoneType::object(), s0), Bool::falseObj()); EXPECT_EQ(Runtime::objectEquals(thread_, s0, NoneType::object()), Bool::falseObj()); } TEST_F(RuntimeTest, ObjectEqualsWithLargeStrReturnsBool) { HandleScope scope(thread_); LargeStr large_str0(&scope, runtime_->newStrFromCStr("foobarbazbumbam")); LargeStr large_str1(&scope, runtime_->newStrFromCStr("foobarbazbumbam")); ASSERT_NE(large_str0, large_str1); EXPECT_EQ(Runtime::objectEquals(thread_, *large_str0, *large_str1), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, *large_str0, runtime_->newStrFromCStr("hello world!")), Bool::falseObj()); } TEST_F(RuntimeTest, ObjectEqualsWithImmediatesReturnsBool) { EXPECT_EQ( Runtime::objectEquals(thread_, NoneType::object(), NoneType::object()), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, SmallInt::fromWord(-88), SmallInt::fromWord(-88)), Bool::trueObj()); EXPECT_EQ(Runtime::objectEquals(thread_, NoneType::object(), NotImplementedType::object()), Bool::falseObj()); EXPECT_EQ(Runtime::objectEquals(thread_, SmallInt::fromWord(11), SmallInt::fromWord(-11)), Bool::falseObj()); } TEST_F(RuntimeTest, ObjectEqualsWithIntAndBoolReturnsBool) { EXPECT_EQ( Runtime::objectEquals(thread_, SmallInt::fromWord(0), Bool::falseObj()), Bool::trueObj()); EXPECT_EQ( Runtime::objectEquals(thread_, SmallInt::fromWord(1), Bool::trueObj()), Bool::trueObj()); EXPECT_EQ( Runtime::objectEquals(thread_, Bool::falseObj(), SmallInt::fromWord(0)), Bool::trueObj()); EXPECT_EQ( Runtime::objectEquals(thread_, Bool::trueObj(), SmallInt::fromWord(1)), Bool::trueObj()); EXPECT_EQ( Runtime::objectEquals(thread_, Bool::falseObj(), SmallInt::fromWord(1)), Bool::falseObj()); EXPECT_EQ( Runtime::objectEquals(thread_, SmallInt::fromWord(0), Bool::trueObj()), Bool::falseObj()); } static ALIGN_16 RawObject testPrintTraceback(Thread* thread, Arguments) { TemporaryDirectory tempdir; std::string temp = tempdir.path + "traceback"; int fd = File::open(temp.c_str(), File::kCreate | File::kWriteOnly, 0777); DCHECK(fd != -1, "error opening file"); thread->runtime()->printTraceback(thread, fd); DCHECK(File::close(fd) == 0, "error closing file"); word length; FILE* fp = std::fopen(temp.c_str(), "r"); unique_c_ptr<byte> traceback(OS::readFile(fp, &length)); std::fclose(fp); return thread->runtime()->newStrWithAll(View<byte>(traceback.get(), length)); } TEST_F(RuntimeTest, PrintTracebackPrintsToFileDescriptor) { addBuiltin("traceback", testPrintTraceback, {nullptr, 0}, 0); ASSERT_FALSE(runFromCStr(runtime_, R"( def foo(x, y): # emptyline result = bar(y, x) return result def bar(y, x): local = 42 return traceback() result = foo('a', 99) )") .isError()); const char* expected = R"(Stack (most recent call first): File "", line ??? in traceback File "<test string>", line 8 in bar File "<test string>", line 4 in foo File "<test string>", line 9 in <module> )"; EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"), expected)); } TEST_F(RuntimeStrTest, StrJoinWithStrSubclassReturnsJoinedString) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C(str): pass elts = (C("a"), C("b"), C("c")) )") .isError()); HandleScope scope(thread_); Str sep(&scope, runtime_->newStrFromCStr(",")); Tuple elts(&scope, mainModuleAt(runtime_, "elts")); Object result(&scope, runtime_->strJoin(thread_, sep, elts, elts.length())); EXPECT_TRUE(isStrEqualsCStr(*result, "a,b,c")); } TEST_F(RuntimeStrTest, StrReplaceWithSmallStrResult) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("1212")); Str old(&scope, runtime_->newStrFromCStr("2")); Str newstr(&scope, runtime_->newStrFromCStr("*")); Object result(&scope, runtime_->strReplace(thread_, str, old, newstr, -1)); EXPECT_TRUE(isStrEqualsCStr(*result, "1*1*")); } TEST_F(RuntimeStrTest, StrReplaceWithSmallStrAndNegativeReplacesAll) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("122")); Str old(&scope, runtime_->newStrFromCStr("2")); Str newstr(&scope, runtime_->newStrFromCStr("*")); Object result(&scope, runtime_->strReplace(thread_, str, old, newstr, -1)); EXPECT_TRUE(isStrEqualsCStr(*result, "1**")); } TEST_F(RuntimeStrTest, StrReplaceWithLargeStrAndNegativeReplacesAll) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("111111121111111111211")); Str old(&scope, runtime_->newStrFromCStr("2")); Str newstr(&scope, runtime_->newStrFromCStr("*")); Object result(&scope, runtime_->strReplace(thread_, str, old, newstr, -1)); EXPECT_TRUE(isStrEqualsCStr(*result, "1111111*1111111111*11")); } TEST_F(RuntimeStrTest, StrReplaceWithLargeStrAndCountReplacesSome) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("11112111111111111211")); Str old(&scope, runtime_->newStrFromCStr("2")); Str newstr(&scope, runtime_->newStrFromCStr("*")); Object result(&scope, runtime_->strReplace(thread_, str, old, newstr, 1)); EXPECT_TRUE(isStrEqualsCStr(*result, "1111*111111111111211")); } TEST_F(RuntimeStrTest, StrReplaceWithSameLengthReplacesSubstr) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("12")); Str old(&scope, runtime_->newStrFromCStr("2")); Str newstr(&scope, runtime_->newStrFromCStr("*")); Object result(&scope, runtime_->strReplace(thread_, str, old, newstr, -1)); EXPECT_TRUE(isStrEqualsCStr(*result, "1*")); } TEST_F(RuntimeStrTest, StrReplaceWithLongerNewReturnsLonger) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("12")); Str old(&scope, runtime_->newStrFromCStr("2")); Str newstr(&scope, runtime_->newStrFromCStr("**")); Object result(&scope, runtime_->strReplace(thread_, str, old, newstr, -1)); EXPECT_TRUE(isStrEqualsCStr(*result, "1**")); } TEST_F(RuntimeStrTest, StrReplaceWithShorterNewReturnsShorter) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("12")); Str old(&scope, runtime_->newStrFromCStr("12")); Str newstr(&scope, runtime_->newStrFromCStr("*")); Object result(&scope, runtime_->strReplace(thread_, str, old, newstr, -1)); EXPECT_TRUE(isStrEqualsCStr(*result, "*")); } TEST_F(RuntimeStrTest, StrReplaceWithPrefixReplacesBeginning) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("12")); Str old(&scope, runtime_->newStrFromCStr("1")); Str newstr(&scope, runtime_->newStrFromCStr("*")); Object result(&scope, runtime_->strReplace(thread_, str, old, newstr, -1)); EXPECT_TRUE(isStrEqualsCStr(*result, "*2")); } TEST_F(RuntimeStrTest, StrReplaceWithInfixReplacesMiddle) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("121")); Str old(&scope, runtime_->newStrFromCStr("2")); Str newstr(&scope, runtime_->newStrFromCStr("*")); Object result(&scope, runtime_->strReplace(thread_, str, old, newstr, -1)); EXPECT_TRUE(isStrEqualsCStr(*result, "1*1")); } TEST_F(RuntimeStrTest, StrReplaceWithPostfixReplacesEnd) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("112")); Str old(&scope, runtime_->newStrFromCStr("2")); Str newstr(&scope, runtime_->newStrFromCStr("*")); Object result(&scope, runtime_->strReplace(thread_, str, old, newstr, -1)); EXPECT_TRUE(isStrEqualsCStr(*result, "11*")); } TEST_F(RuntimeStrTest, StrSliceASCII) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr("hello world goodbye world")); Object slice(&scope, runtime_->strSlice(thread_, str, 2, 10, 2)); EXPECT_TRUE(isStrEqualsCStr(*slice, "lowr")); } TEST_F(RuntimeStrTest, StrSliceUnicode) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr( u8"\u05d0\u05e0\u05d9 \u05dc\u05d0 \u05d0\u05d5\u05d4\u05d1 " u8"\u05e0\u05d7\u05e9\u05d9\u05dd")); Str slice(&scope, runtime_->strSlice(thread_, str, 2, 10, 2)); EXPECT_TRUE(isStrEqualsCStr(*slice, u8"\u05d9\u05dc \u05d5")); } TEST_F(RuntimeStrTest, StrSliceUnicodeWithLargeResult) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr( u8"\u05d0\u05e0\u05d9 \u05dc\u05d0 \u05d0\u05d5\u05d4\u05d1 " u8"\u05e0\u05d7\u05e9\u05d9\u05dd")); Str slice(&scope, runtime_->strSlice(thread_, str, 2, 13, 2)); EXPECT_TRUE(isStrEqualsCStr(*slice, u8"\u05d9\u05dc \u05d5\u05d1\u05e0")); } TEST_F(RuntimeStrTest, StrSliceUnicodeWithStepOne) { HandleScope scope(thread_); Str str(&scope, runtime_->newStrFromCStr(u8"\u05d0\u05e0\u05d9 \u05dc\u05d0 ")); Str slice(&scope, runtime_->strSlice(thread_, str, 2, 5, 1)); EXPECT_TRUE(isStrEqualsCStr(*slice, u8"\u05d9 \u05dc")); } TEST_F(RuntimeTest, BuiltinBaseOfNonEmptyTypeIsTypeItself) { HandleScope scope(thread_); LayoutId layout_id = LayoutId::kUserWarning; Object previous_layout(&scope, runtime_->layoutAt(layout_id)); BuiltinAttribute attrs[] = { {ID(__globals__), 0, AttributeFlags::kReadOnly}, }; Type type(&scope, addBuiltinType(thread_, ID(UserWarning), layout_id, LayoutId::kObject, attrs, /*size=*/kPointerSize, /*basetype=*/true)); EXPECT_EQ(type.builtinBase(), layout_id); runtime_->layoutAtPut(layout_id, *previous_layout); } TEST_F(RuntimeTest, BuiltinBaseOfEmptyTypeIsSuperclass) { HandleScope scope(thread_); LayoutId layout_id = LayoutId::kUserWarning; Object previous_layout(&scope, runtime_->layoutAt(layout_id)); Type type(&scope, addBuiltinType(thread_, ID(UserWarning), layout_id, LayoutId::kObject, kNoAttributes, HeapObject::kSize, /*basetype=*/true)); EXPECT_EQ(type.builtinBase(), LayoutId::kObject); runtime_->layoutAtPut(layout_id, *previous_layout); } TEST_F(RuntimeTest, NonModuleInModulesDoesNotCrash) { HandleScope scope(thread_); Object not_a_module(&scope, runtime_->newInt(42)); Str name(&scope, runtime_->newStrFromCStr("a_valid_module_name")); Dict modules(&scope, runtime_->modules()); dictAtPutByStr(thread_, modules, name, not_a_module); Object result(&scope, runtime_->findModule(name)); EXPECT_EQ(result, not_a_module); } TEST_F(RuntimeStrArrayTest, AddCodePointAppendsValidUTF8) { HandleScope scope(thread_); StrArray array(&scope, runtime_->newStrArray()); runtime_->strArrayAddCodePoint(thread_, array, 'a'); runtime_->strArrayAddCodePoint(thread_, array, ' '); runtime_->strArrayAddCodePoint(thread_, array, 0xe9); runtime_->strArrayAddCodePoint(thread_, array, ' '); runtime_->strArrayAddCodePoint(thread_, array, 0x2cc0); runtime_->strArrayAddCodePoint(thread_, array, ' '); runtime_->strArrayAddCodePoint(thread_, array, 0x1f192); EXPECT_TRUE(isStrEqualsCStr(runtime_->strFromStrArray(array), "a \u00e9 \u2cc0 \U0001f192")); } TEST_F(RuntimeStrArrayTest, NewStrArrayReturnsEmptyStrArray) { HandleScope scope(thread_); Object obj(&scope, runtime_->newStrArray()); ASSERT_TRUE(obj.isStrArray()); StrArray str_arr(&scope, *obj); EXPECT_EQ(str_arr.numItems(), 0); EXPECT_EQ(str_arr.capacity(), 0); } TEST_F(RuntimeStrArrayTest, EnsureCapacitySetsProperCapacity) { HandleScope scope(thread_); StrArray array(&scope, runtime_->newStrArray()); word length = 1; word expected_capacity = 16; runtime_->strArrayEnsureCapacity(thread_, array, length); EXPECT_EQ(array.capacity(), expected_capacity); length = 17; expected_capacity = 24; runtime_->strArrayEnsureCapacity(thread_, array, length); EXPECT_EQ(array.capacity(), expected_capacity); length = 40; expected_capacity = 40; runtime_->strArrayEnsureCapacity(thread_, array, length); EXPECT_EQ(array.capacity(), expected_capacity); } TEST_F(RuntimeStrArrayTest, NewStrFromEmptyStrArrayReturnsEmptyStr) { HandleScope scope(thread_); StrArray array(&scope, runtime_->newStrArray()); EXPECT_EQ(runtime_->strFromStrArray(array), Str::empty()); } TEST_F(RuntimeStrArrayTest, AppendStrAppendsValidUTF8) { HandleScope scope(thread_); StrArray array(&scope, runtime_->newStrArray()); Str one(&scope, runtime_->newStrFromCStr("a\xC3\xA9")); Str two(&scope, runtime_->newStrFromCStr("\xE2\xB3\x80\xF0\x9F\x86\x92")); runtime_->strArrayAddStr(thread_, array, one); runtime_->strArrayAddStr(thread_, array, two); EXPECT_EQ(array.numItems(), 10); EXPECT_TRUE(isStrEqualsCStr(runtime_->strFromStrArray(array), "a\xC3\xA9\xE2\xB3\x80\xF0\x9F\x86\x92")); } TEST_F(RuntimeStrArrayTest, AddStrArrayAppendsValidUTF8) { HandleScope scope(thread_); StrArray array(&scope, runtime_->newStrArray()); StrArray one(&scope, runtime_->newStrArray()); Str one_contents(&scope, runtime_->newStrFromCStr("a\xC3\xA9")); runtime_->strArrayAddStr(thread_, one, one_contents); StrArray two(&scope, runtime_->newStrArray()); Str two_contents(&scope, runtime_->newStrFromCStr("\xE2\xB3\x80\xF0\x9F\x86\x92")); runtime_->strArrayAddStr(thread_, two, two_contents); runtime_->strArrayAddStrArray(thread_, array, one); runtime_->strArrayAddStrArray(thread_, array, two); EXPECT_EQ(array.numItems(), 10); EXPECT_TRUE(isStrEqualsCStr(runtime_->strFromStrArray(array), "a\xC3\xA9\xE2\xB3\x80\xF0\x9F\x86\x92")); } TEST_F(RuntimeStrArrayTest, AddASCIIAppendsASCII) { HandleScope scope(thread_); StrArray array(&scope, runtime_->newStrArray()); runtime_->strArrayAddASCII(thread_, array, 'h'); runtime_->strArrayAddASCII(thread_, array, 'i'); EXPECT_EQ(array.numItems(), 2); EXPECT_TRUE(isStrEqualsCStr(runtime_->strFromStrArray(array), "hi")); } TEST(RuntimeTestNoFixture, DestructorRestoresSignalHandlers) { Runtime* runtime = createTestRuntime(); EXPECT_NE(OS::signalHandler(SIGINT), SIG_DFL); delete runtime; EXPECT_EQ(OS::signalHandler(SIGINT), SIG_DFL); } TEST_F(RuntimeTest, AllocateForMachineCodeReturnsTrue) { uword address = 0; EXPECT_TRUE(runtime_->allocateForMachineCode(1 * kKiB, &address)); EXPECT_NE(address, uword{0}); } TEST_F(RuntimeTest, AllocateForMachineCodeReturnsSequentialAddresses) { uword address = 0; runtime_->allocateForMachineCode(1 * kKiB, &address); EXPECT_NE(address, uword{0}); uword address2 = 0; runtime_->allocateForMachineCode(2 * kKiB, &address2); EXPECT_EQ(address2, address + 1 * kKiB); } } // namespace testing } // namespace py