runtime/list-builtins-test.cpp (942 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) #include "list-builtins.h" #include "gtest/gtest.h" #include "builtins-module.h" #include "builtins.h" #include "objects.h" #include "runtime.h" #include "test-utils.h" namespace py { namespace testing { using ListBuiltinsTest = RuntimeFixture; using ListIteratorBuiltinsTest = RuntimeFixture; TEST_F(ListBuiltinsTest, CopyWithListReturnsNewInstance) { ASSERT_FALSE(runFromCStr(runtime_, R"( l = [1, 2, 3] result = list.copy(l) )") .isError()); HandleScope scope(thread_); Object list(&scope, mainModuleAt(runtime_, "l")); EXPECT_TRUE(list.isList()); Object result_obj(&scope, mainModuleAt(runtime_, "result")); EXPECT_TRUE(result_obj.isList()); List result(&scope, *result_obj); EXPECT_NE(*list, *result); EXPECT_EQ(result.numItems(), 3); } TEST_F(ListBuiltinsTest, DunderEqReturnsTrue) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = list.__eq__([1, 2, 3], [1, 2, 3]) )") .isError()); EXPECT_EQ(mainModuleAt(runtime_, "result"), RawBool::trueObj()); } TEST_F(ListBuiltinsTest, DunderEqWithSameListReturnsTrue) { ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: def __eq__(self, other): return False a = [1, 2, 3] result = list.__eq__(a, a) )") .isError()); EXPECT_EQ(mainModuleAt(runtime_, "result"), RawBool::trueObj()); } TEST_F(ListBuiltinsTest, DunderEqWithSameIdentityElementsReturnsTrue) { ASSERT_FALSE(runFromCStr(runtime_, R"( nan = float("nan") result = list.__eq__([nan], [nan]) )") .isError()); EXPECT_EQ(mainModuleAt(runtime_, "result"), RawBool::trueObj()); } TEST_F(ListBuiltinsTest, DunderEqWithEqualElementsReturnsTrue) { ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: def __init__(self, value): self.value = value def __eq__(self, other): return type(self.value).__eq__(self.value, other.value) a = Foo(1) b = Foo(1) result = list.__eq__([a], [b]) )") .isError()); EXPECT_EQ(mainModuleAt(runtime_, "result"), RawBool::trueObj()); } TEST_F(ListBuiltinsTest, DunderEqWithDifferentSizeReturnsFalse) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = list.__eq__([1, 2, 3], [1, 2]) )") .isError()); EXPECT_EQ(mainModuleAt(runtime_, "result"), RawBool::falseObj()); } TEST_F(ListBuiltinsTest, DunderEqWithDifferentValuesReturnsFalse) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = list.__eq__([1, 2, 3], [1, 2, 4]) )") .isError()); EXPECT_EQ(mainModuleAt(runtime_, "result"), RawBool::falseObj()); } TEST_F(ListBuiltinsTest, DunderEqWithNonListRhsReturnsNotImplemented) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = list.__eq__([1, 2, 3], (1, 2, 3)); )") .isError()); EXPECT_TRUE(mainModuleAt(runtime_, "result").isNotImplementedType()); } TEST_F(ListBuiltinsTest, DunderInitFromList) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = list([1, 2]) )") .isError()); HandleScope scope(thread_); List a(&scope, mainModuleAt(runtime_, "a")); ASSERT_EQ(a.numItems(), 2); EXPECT_TRUE(isIntEqualsWord(a.at(0), 1)); EXPECT_TRUE(isIntEqualsWord(a.at(1), 2)); } TEST_F(ListBuiltinsTest, NewListIsNotASingleton) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = list() is not list() b = list([1, 2]) is not list([1, 2]) )") .isError()); HandleScope scope(thread_); Bool a(&scope, mainModuleAt(runtime_, "a")); Bool b(&scope, mainModuleAt(runtime_, "b")); EXPECT_TRUE(a.value()); EXPECT_TRUE(b.value()); } TEST_F(ListBuiltinsTest, AddToNonEmptyList) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( a = [1, 2] b = [3, 4, 5] c = a + b )") .isError()); Object c(&scope, mainModuleAt(runtime_, "c")); ASSERT_TRUE(c.isList()); List list(&scope, List::cast(*c)); EXPECT_TRUE(isIntEqualsWord(list.at(0), 1)); EXPECT_TRUE(isIntEqualsWord(list.at(1), 2)); EXPECT_TRUE(isIntEqualsWord(list.at(2), 3)); EXPECT_TRUE(isIntEqualsWord(list.at(3), 4)); EXPECT_TRUE(isIntEqualsWord(list.at(4), 5)); } TEST_F(ListBuiltinsTest, AddToEmptyList) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( a = [] b = [1, 2, 3] c = a + b )") .isError()); Object c(&scope, mainModuleAt(runtime_, "c")); ASSERT_TRUE(c.isList()); List list(&scope, List::cast(*c)); EXPECT_TRUE(isIntEqualsWord(list.at(0), 1)); EXPECT_TRUE(isIntEqualsWord(list.at(1), 2)); EXPECT_TRUE(isIntEqualsWord(list.at(2), 3)); } TEST_F(ListBuiltinsTest, AddWithListSubclassReturnsList) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C(list): pass a = [] b = C([1, 2, 3]) c = a + b )") .isError()); Object c(&scope, mainModuleAt(runtime_, "c")); EXPECT_PYLIST_EQ(c, {1, 2, 3}); } TEST_F(ListBuiltinsTest, AddListToTupleRaisesTypeError) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( a = [1, 2, 3] b = (4, 5, 6) c = a + b )"), LayoutId::kTypeError, "can only concatenate list to list")); } TEST_F(ListBuiltinsTest, DunderContainsWithContainedElementReturnsTrue) { HandleScope scope(thread_); Int value0(&scope, runtime_->newInt(1)); Bool value1(&scope, RawBool::falseObj()); Str value2(&scope, runtime_->newStrFromCStr("hello")); List list(&scope, runtime_->newList()); runtime_->listAdd(thread_, list, value0); runtime_->listAdd(thread_, list, value1); runtime_->listAdd(thread_, list, value2); EXPECT_EQ(runBuiltin(METH(list, __contains__), list, value0), RawBool::trueObj()); EXPECT_EQ(runBuiltin(METH(list, __contains__), list, value1), RawBool::trueObj()); EXPECT_EQ(runBuiltin(METH(list, __contains__), list, value2), RawBool::trueObj()); } TEST_F(ListBuiltinsTest, DunderContainsWithUncontainedElementReturnsFalse) { HandleScope scope(thread_); Int value0(&scope, runtime_->newInt(7)); NoneType value1(&scope, RawNoneType::object()); List list(&scope, runtime_->newList()); runtime_->listAdd(thread_, list, value0); runtime_->listAdd(thread_, list, value1); Int value2(&scope, runtime_->newInt(42)); Bool value3(&scope, RawBool::trueObj()); EXPECT_EQ(runBuiltin(METH(list, __contains__), list, value2), RawBool::falseObj()); EXPECT_EQ(runBuiltin(METH(list, __contains__), list, value3), RawBool::falseObj()); } TEST_F(ListBuiltinsTest, DunderContainsWithIdenticalObjectReturnsTrue) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: def __eq__(self, other): return False value = Foo() list = [value] )") .isError()); Object value(&scope, mainModuleAt(runtime_, "value")); List list(&scope, mainModuleAt(runtime_, "list")); EXPECT_EQ(runBuiltin(METH(list, __contains__), list, value), RawBool::trueObj()); } TEST_F(ListBuiltinsTest, DunderContainsWithNonIdenticalEqualKeyObjectReturnsTrue) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: def __eq__(self, other): return True value = Foo() list = [None] )") .isError()); Object value(&scope, mainModuleAt(runtime_, "value")); List list(&scope, mainModuleAt(runtime_, "list")); EXPECT_EQ(runBuiltin(METH(list, __contains__), list, value), RawBool::trueObj()); } TEST_F(ListBuiltinsTest, DunderContainsWithNonIdenticalEqualListObjectReturnsFalse) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: def __eq__(self, other): return True class Bar: def __eq__(self, other): return False value0 = Foo() value1 = Bar() list = [value0] )") .isError()); Object value1(&scope, mainModuleAt(runtime_, "value1")); List list(&scope, mainModuleAt(runtime_, "list")); EXPECT_EQ(runBuiltin(METH(list, __contains__), list, value1), RawBool::falseObj()); } TEST_F(ListBuiltinsTest, DunderContainsWithRaisingEqPropagatesException) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: def __eq__(self, other): raise UserWarning("") value = Foo() list = [None] )") .isError()); Object value(&scope, mainModuleAt(runtime_, "value")); List list(&scope, mainModuleAt(runtime_, "list")); Object result(&scope, runBuiltin(METH(list, __contains__), list, value)); EXPECT_TRUE(raised(*result, LayoutId::kUserWarning)); } TEST_F(ListBuiltinsTest, DunderContainsWithRaisingDunderBoolPropagatesException) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: def __bool__(self): raise UserWarning("") class Bar: def __eq__(self, other): return Foo() value = Bar() list = [None] )") .isError()); Object value(&scope, mainModuleAt(runtime_, "value")); List list(&scope, mainModuleAt(runtime_, "list")); EXPECT_TRUE(raised(runBuiltin(METH(list, __contains__), list, value), LayoutId::kUserWarning)); } TEST_F(ListBuiltinsTest, DunderContainsWithNonListSelfRaisesTypeError) { HandleScope scope(thread_); Int i(&scope, SmallInt::fromWord(3)); Object result(&scope, runBuiltin(METH(list, __contains__), i, i)); EXPECT_TRUE(raised(*result, LayoutId::kTypeError)); } TEST_F(ListBuiltinsTest, ListInsertWithMissingArgumentsRaisesTypeError) { EXPECT_TRUE(raisedWithStr( runFromCStr(runtime_, "[1, 2].insert()"), LayoutId::kTypeError, "'list.insert' takes min 3 positional arguments but 1 given")); } TEST_F(ListBuiltinsTest, ListInsertWithNonIntIndexRaisesTypeError) { EXPECT_TRUE(raisedWithStr( runFromCStr(runtime_, "[1, 2].insert({}, 3)"), LayoutId::kTypeError, "'dict' object cannot be interpreted as an integer")); } TEST_F(ListBuiltinsTest, ListInsertWithLargeIntIndexRaisesTypeError) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, "[1, 2].insert(2 ** 63, 1)"), LayoutId::kOverflowError, "Python int too large to convert to C ssize_t")); } TEST_F(ListBuiltinsTest, ListInsertWithBoolIndexInsertsAtInt) { HandleScope scope(thread_); List self(&scope, runtime_->newList()); Object value(&scope, SmallInt::fromWord(3)); runtime_->listAdd(thread_, self, value); runtime_->listAdd(thread_, self, value); Object fals(&scope, Bool::falseObj()); Object tru(&scope, Bool::trueObj()); Object result(&scope, runBuiltin(METH(list, insert), self, tru, tru)); EXPECT_EQ(result, NoneType::object()); result = runBuiltin(METH(list, insert), self, fals, fals); EXPECT_EQ(result, NoneType::object()); ASSERT_EQ(self.numItems(), 4); EXPECT_EQ(self.at(0), fals); EXPECT_EQ(self.at(1), value); EXPECT_EQ(self.at(2), tru); EXPECT_EQ(self.at(3), value); } TEST_F(ListBuiltinsTest, ListInsertWithIntSubclassInsertsAtInt) { ASSERT_FALSE(runFromCStr(runtime_, R"( class N(int): pass a = [0, 0, 0, 0, 0] b = N(3) )") .isError()); HandleScope scope(thread_); List self(&scope, mainModuleAt(runtime_, "a")); Object index(&scope, mainModuleAt(runtime_, "b")); Object value(&scope, SmallInt::fromWord(1)); Object result(&scope, runBuiltin(METH(list, insert), self, index, value)); EXPECT_EQ(result, NoneType::object()); EXPECT_EQ(self.numItems(), 6); EXPECT_EQ(self.at(3), value); } TEST_F(ListBuiltinsTest, ListRemove) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = [5, 4, 3, 2, 1] a.remove(2) a.remove(5) )") .isError()); HandleScope scope(thread_); Object a(&scope, mainModuleAt(runtime_, "a")); EXPECT_PYLIST_EQ(a, {4, 3, 1}); } TEST_F(ListBuiltinsTest, ListRemoveWithDuplicateItemsRemovesFirstMatchingItem) { HandleScope scope(thread_); Int value0(&scope, runtime_->newInt(0)); Int value1(&scope, runtime_->newInt(1)); Int value2(&scope, runtime_->newInt(2)); List list(&scope, runtime_->newList()); runtime_->listAdd(thread_, list, value0); runtime_->listAdd(thread_, list, value1); runtime_->listAdd(thread_, list, value2); runtime_->listAdd(thread_, list, value1); runtime_->listAdd(thread_, list, value0); EXPECT_EQ(list.numItems(), 5); runBuiltin(METH(list, remove), list, value1); ASSERT_EQ(list.numItems(), 4); EXPECT_EQ(list.at(0), value0); EXPECT_EQ(list.at(1), value2); EXPECT_EQ(list.at(2), value1); EXPECT_EQ(list.at(3), value0); } TEST_F(ListBuiltinsTest, ListRemoveWithIdenticalObjectGetsRemoved) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __eq__(self, other): return False value = C() list = [value] )") .isError()); Object value(&scope, mainModuleAt(runtime_, "value")); List list(&scope, mainModuleAt(runtime_, "list")); EXPECT_EQ(list.numItems(), 1); runBuiltin(METH(list, remove), list, value); EXPECT_EQ(list.numItems(), 0); } TEST_F(ListBuiltinsTest, ListRemoveWithNonIdenticalEqualObjectInListGetsRemoved) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __eq__(self, other): return True list = [C()] )") .isError()); Object value(&scope, NoneType::object()); List list(&scope, mainModuleAt(runtime_, "list")); EXPECT_EQ(list.numItems(), 1); runBuiltin(METH(list, remove), list, value); EXPECT_EQ(list.numItems(), 0); } TEST_F(ListBuiltinsTest, ListRemoveWithNonIdenticalEqualObjectAsKeyRaisesValueError) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __eq__(self, other): return True class D: def __eq__(self, other): return False value = C() list = [D()] )") .isError()); Object value(&scope, mainModuleAt(runtime_, "value")); List list(&scope, mainModuleAt(runtime_, "list")); Object result(&scope, runBuiltin(METH(list, remove), list, value)); EXPECT_TRUE(raised(*result, LayoutId::kValueError)); } TEST_F(ListBuiltinsTest, ListRemoveWithRaisingDunderEqualPropagatesException) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo: def __eq__(self, other): raise UserWarning('') value = Foo() list = [None] )") .isError()); Object value(&scope, mainModuleAt(runtime_, "value")); List list(&scope, mainModuleAt(runtime_, "list")); Object result(&scope, runBuiltin(METH(list, remove), list, value)); EXPECT_TRUE(raised(*result, LayoutId::kUserWarning)); } TEST_F(ListBuiltinsTest, ListRemoveWithRaisingDunderBoolPropagatesException) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class C: def __bool__(self): raise UserWarning('foo') class D: def __eq__(self, other): return C() value = D() list = [None] )") .isError()); Object value(&scope, mainModuleAt(runtime_, "value")); List list(&scope, mainModuleAt(runtime_, "list")); EXPECT_TRUE(raisedWithStr(runBuiltin(METH(list, remove), list, value), LayoutId::kUserWarning, "foo")); } TEST_F(ListBuiltinsTest, ReplicateList) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = [1, 2, 3] * 3 )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_PYLIST_EQ(result, {1, 2, 3, 1, 2, 3, 1, 2, 3}); } TEST_F(ListBuiltinsTest, ReplicateListWithNegativeRhsReturnsEmptyList) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = [1, 2, 3] * -3 )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_PYLIST_EQ(result, {}); } TEST_F(ListBuiltinsTest, SliceWithPositiveStepReturnsForwardsList) { HandleScope scope(thread_); List list1(&scope, listFromRange(1, 6)); // Test [2:] List test(&scope, listSlice(thread_, list1, 2, 5, 1)); ASSERT_EQ(test.numItems(), 3); EXPECT_TRUE(isIntEqualsWord(test.at(0), 3)); EXPECT_TRUE(isIntEqualsWord(test.at(1), 4)); EXPECT_TRUE(isIntEqualsWord(test.at(2), 5)); } TEST_F(ListBuiltinsTest, SliceWithNegativeStepReturnsBackwardsList) { HandleScope scope(thread_); List list1(&scope, listFromRange(1, 6)); // Test [::-2] List test(&scope, listSlice(thread_, list1, 4, -1, -2)); ASSERT_EQ(test.numItems(), 3); EXPECT_TRUE(isIntEqualsWord(test.at(0), 5)); EXPECT_TRUE(isIntEqualsWord(test.at(1), 3)); EXPECT_TRUE(isIntEqualsWord(test.at(2), 1)); } TEST_F(ListBuiltinsTest, IdenticalSliceIsCopy) { HandleScope scope(thread_); List list1(&scope, listFromRange(1, 6)); // Test: t[::] is t List test(&scope, listSlice(thread_, list1, 0, 5, 1)); ASSERT_EQ(test.numItems(), 5); EXPECT_TRUE(isIntEqualsWord(test.at(0), 1)); EXPECT_TRUE(isIntEqualsWord(test.at(4), 5)); ASSERT_NE(*test, *list1); } TEST_F(ListBuiltinsTest, DelitemWithInvalidNegativeIndexRaisesIndexError) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( l = [1, 2, 3] del l[-4] )"), LayoutId::kIndexError, "list assignment index out of range")); } TEST_F(ListBuiltinsTest, DelitemWithInvalidIndexRaisesIndexError) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( l = [1, 2, 3] del l[5] )"), LayoutId::kIndexError, "list assignment index out of range")); } TEST_F(ListBuiltinsTest, DelitemWithTooFewArgumentsRaisesTypeError) { EXPECT_TRUE(raisedWithStr( runFromCStr(runtime_, R"( [].__delitem__() )"), LayoutId::kTypeError, "'list.__delitem__' takes min 2 positional arguments but 1 given")); } TEST_F(ListBuiltinsTest, DelitemWithTooManyArgumentsRaisesTypeError) { EXPECT_TRUE(raisedWithStr( runFromCStr(runtime_, R"( [].__delitem__(1, 2) )"), LayoutId::kTypeError, "'list.__delitem__' takes max 2 positional arguments but 3 given")); } TEST_F(ListBuiltinsTest, DelitemWithNonIntegralIndexRaisesTypeError) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( [].__delitem__("test") )"), LayoutId::kTypeError, "list indices must be integers or slices")); } TEST_F(ListBuiltinsTest, NonTypeInDunderNew) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( list.__new__(1) )"), LayoutId::kTypeError, "not a type object")); } TEST_F(ListBuiltinsTest, NonSubclassInDunderNew) { EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"( class Foo: pass list.__new__(Foo) )"), LayoutId::kTypeError, "not a subtype of list")); } TEST_F(ListBuiltinsTest, SubclassList) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( class Foo(): def __init__(self): self.a = "a" class Bar(Foo, list): pass a = Bar() a.append(1) test1, test2 = a[0], a.a a.insert(0, 2) test3, test4 = a[0], a[1] a.pop() test5 = a[0] a.remove(2) test6 = len(a) )") .isError()); Object test1(&scope, mainModuleAt(runtime_, "test1")); Object test2(&scope, mainModuleAt(runtime_, "test2")); Object test3(&scope, mainModuleAt(runtime_, "test3")); Object test4(&scope, mainModuleAt(runtime_, "test4")); Object test5(&scope, mainModuleAt(runtime_, "test5")); Object test6(&scope, mainModuleAt(runtime_, "test6")); EXPECT_EQ(*test1, SmallInt::fromWord(1)); EXPECT_EQ(*test2, SmallStr::fromCStr("a")); EXPECT_EQ(*test3, SmallInt::fromWord(2)); EXPECT_EQ(*test4, SmallInt::fromWord(1)); EXPECT_EQ(*test5, SmallInt::fromWord(2)); EXPECT_EQ(*test6, SmallInt::fromWord(0)); } TEST_F(ListBuiltinsTest, Delitem) { HandleScope scope(thread_); ASSERT_FALSE(runFromCStr(runtime_, R"( a = [42,'foo', 'bar'] del a[2] del a[0] l = len(a) e = a[0] )") .isError()); Object len(&scope, mainModuleAt(runtime_, "l")); Object element(&scope, mainModuleAt(runtime_, "e")); EXPECT_EQ(*len, SmallInt::fromWord(1)); EXPECT_EQ(*element, SmallStr::fromCStr("foo")); } TEST_F(ListBuiltinsTest, DunderIterReturnsListIter) { HandleScope scope(thread_); List empty_list(&scope, listFromRange(0, 0)); Object iter(&scope, runBuiltin(METH(list, __iter__), empty_list)); ASSERT_TRUE(iter.isListIterator()); } TEST_F(ListIteratorBuiltinsTest, CallDunderNext) { HandleScope scope(thread_); List empty_list(&scope, listFromRange(0, 2)); Object iter(&scope, runBuiltin(METH(list, __iter__), empty_list)); ASSERT_TRUE(iter.isListIterator()); Object item1(&scope, runBuiltin(METH(list_iterator, __next__), iter)); EXPECT_TRUE(isIntEqualsWord(*item1, 0)); Object item2(&scope, runBuiltin(METH(list_iterator, __next__), iter)); EXPECT_TRUE(isIntEqualsWord(*item2, 1)); } TEST_F(ListIteratorBuiltinsTest, DunderIterReturnsSelf) { HandleScope scope(thread_); List empty_list(&scope, listFromRange(0, 0)); Object iter(&scope, runBuiltin(METH(list, __iter__), empty_list)); ASSERT_TRUE(iter.isListIterator()); // Now call __iter__ on the iterator object Object result(&scope, runBuiltin(METH(list_iterator, __iter__), iter)); ASSERT_EQ(*result, *iter); } TEST_F(ListIteratorBuiltinsTest, DunderLengthHintOnEmptyListIterator) { HandleScope scope(thread_); List empty_list(&scope, listFromRange(0, 0)); Object iter(&scope, runBuiltin(METH(list, __iter__), empty_list)); ASSERT_TRUE(iter.isListIterator()); Object length_hint(&scope, runBuiltin(METH(list_iterator, __length_hint__), iter)); EXPECT_TRUE(isIntEqualsWord(*length_hint, 0)); } TEST_F(ListIteratorBuiltinsTest, DunderLengthHintOnConsumedListIterator) { HandleScope scope(thread_); List list(&scope, listFromRange(0, 1)); Object iter(&scope, runBuiltin(METH(list, __iter__), list)); ASSERT_TRUE(iter.isListIterator()); Object length_hint1(&scope, runBuiltin(METH(list_iterator, __length_hint__), iter)); EXPECT_TRUE(isIntEqualsWord(*length_hint1, 1)); // Consume the iterator Object item1(&scope, runBuiltin(METH(list_iterator, __next__), iter)); EXPECT_TRUE(isIntEqualsWord(*item1, 0)); Object length_hint2(&scope, runBuiltin(METH(list_iterator, __length_hint__), iter)); EXPECT_TRUE(isIntEqualsWord(*length_hint2, 0)); } TEST_F(ListBuiltinsTest, InsertToList) { HandleScope scope(thread_); List list(&scope, runtime_->newList()); for (int i = 0; i < 9; i++) { if (i == 1 || i == 6) { continue; } Object value(&scope, SmallInt::fromWord(i)); runtime_->listAdd(thread_, list, value); } EXPECT_FALSE(isIntEqualsWord(list.at(1), 1)); EXPECT_FALSE(isIntEqualsWord(list.at(6), 6)); Object value2(&scope, SmallInt::fromWord(1)); listInsert(thread_, list, value2, 1); Object value12(&scope, SmallInt::fromWord(6)); listInsert(thread_, list, value12, 6); EXPECT_PYLIST_EQ(list, {0, 1, 2, 3, 4, 5, 6, 7, 8}); } TEST_F(ListBuiltinsTest, InsertToListBounds) { HandleScope scope(thread_); List list(&scope, runtime_->newList()); for (int i = 0; i < 10; i++) { Object value(&scope, SmallInt::fromWord(i)); runtime_->listAdd(thread_, list, value); } ASSERT_EQ(list.numItems(), 10); Object value100(&scope, SmallInt::fromWord(100)); listInsert(thread_, list, value100, 100); ASSERT_EQ(list.numItems(), 11); EXPECT_TRUE(isIntEqualsWord(list.at(10), 100)); Object value0(&scope, SmallInt::fromWord(400)); listInsert(thread_, list, value0, 0); ASSERT_EQ(list.numItems(), 12); EXPECT_TRUE(isIntEqualsWord(list.at(0), 400)); Object value_n(&scope, SmallInt::fromWord(-10)); listInsert(thread_, list, value_n, -10); ASSERT_EQ(list.numItems(), 13); EXPECT_TRUE(isIntEqualsWord(list.at(2), -10)); } TEST_F(ListBuiltinsTest, PopList) { HandleScope scope(thread_); List list(&scope, runtime_->newList()); for (int i = 0; i < 16; i++) { Object value(&scope, SmallInt::fromWord(i)); runtime_->listAdd(thread_, list, value); } ASSERT_EQ(list.numItems(), 16); // Pop from the end RawObject res1 = listPop(thread_, list, 15); ASSERT_EQ(list.numItems(), 15); EXPECT_TRUE(isIntEqualsWord(list.at(14), 14)); EXPECT_TRUE(isIntEqualsWord(res1, 15)); // Pop elements from 5 - 10 for (int i = 0; i < 5; i++) { RawObject res5 = listPop(thread_, list, 5); EXPECT_TRUE(isIntEqualsWord(res5, i + 5)); } ASSERT_EQ(list.numItems(), 10); for (int i = 0; i < 5; i++) { EXPECT_TRUE(isIntEqualsWord(list.at(i), i)); } for (int i = 5; i < 10; i++) { EXPECT_TRUE(isIntEqualsWord(list.at(i), i + 5)); } // Pop element 0 RawObject res0 = listPop(thread_, list, 0); ASSERT_EQ(list.numItems(), 9); EXPECT_TRUE(isIntEqualsWord(list.at(0), 1)); EXPECT_TRUE(isIntEqualsWord(res0, 0)); } TEST_F(ListBuiltinsTest, ExtendTuple) { HandleScope scope(thread_); List list(&scope, runtime_->newList()); Tuple object_array0(&scope, runtime_->emptyTuple()); for (int i = 0; i < 4; i++) { Object value(&scope, SmallInt::fromWord(i)); runtime_->listAdd(thread_, list, value); } listExtend(Thread::current(), list, object_array0, 0); EXPECT_EQ(list.numItems(), 4); Object none(&scope, NoneType::object()); Tuple object_array1(&scope, runtime_->newTupleWith1(none)); listExtend(Thread::current(), list, object_array1, 1); ASSERT_GE(list.numItems(), 5); ASSERT_TRUE(list.at(4).isNoneType()); MutableTuple object_array16(&scope, runtime_->newMutableTuple(16)); for (word i = 0; i < 4; i++) { object_array16.atPut(i, SmallInt::fromWord(i)); } Tuple object_array_immutable(&scope, object_array16.becomeImmutable()); listExtend(Thread::current(), list, object_array_immutable, 4); ASSERT_GE(list.numItems(), 9); EXPECT_EQ(list.at(5), SmallInt::fromWord(0)); EXPECT_EQ(list.at(6), SmallInt::fromWord(1)); EXPECT_EQ(list.at(7), SmallInt::fromWord(2)); EXPECT_EQ(list.at(8), SmallInt::fromWord(3)); } TEST_F(ListBuiltinsTest, SortEmptyListSucceeds) { HandleScope scope(thread_); List empty(&scope, runtime_->newList()); ASSERT_EQ(listSort(thread_, empty), NoneType::object()); } TEST_F(ListBuiltinsTest, SortSingleElementListSucceeds) { HandleScope scope(thread_); List list(&scope, runtime_->newList()); Object elt(&scope, SmallInt::fromWord(5)); runtime_->listAdd(thread_, list, elt); ASSERT_EQ(listSort(thread_, list), NoneType::object()); EXPECT_EQ(list.numItems(), 1); EXPECT_EQ(list.at(0), *elt); } TEST_F(ListBuiltinsTest, SortMultiElementListSucceeds) { HandleScope scope(thread_); List list(&scope, runtime_->newList()); Object elt3(&scope, SmallInt::fromWord(3)); runtime_->listAdd(thread_, list, elt3); Object elt2(&scope, SmallInt::fromWord(2)); runtime_->listAdd(thread_, list, elt2); Object elt1(&scope, SmallInt::fromWord(1)); runtime_->listAdd(thread_, list, elt1); ASSERT_EQ(listSort(thread_, list), NoneType::object()); EXPECT_EQ(list.numItems(), 3); EXPECT_PYLIST_EQ(list, {1, 2, 3}); } TEST_F(ListBuiltinsTest, SortMultiElementListSucceeds2) { HandleScope scope(thread_); List list(&scope, runtime_->newList()); Object elt3(&scope, SmallInt::fromWord(1)); runtime_->listAdd(thread_, list, elt3); Object elt2(&scope, SmallInt::fromWord(3)); runtime_->listAdd(thread_, list, elt2); Object elt1(&scope, SmallInt::fromWord(2)); runtime_->listAdd(thread_, list, elt1); ASSERT_EQ(listSort(thread_, list), NoneType::object()); EXPECT_EQ(list.numItems(), 3); EXPECT_PYLIST_EQ(list, {1, 2, 3}); } TEST_F(ListBuiltinsTest, SortIsStable) { HandleScope scope(thread_); List list(&scope, runtime_->newList()); Object elt4(&scope, runtime_->newStrFromCStr("q")); runtime_->listAdd(thread_, list, elt4); Object elt3(&scope, runtime_->newStrFromCStr("world")); runtime_->listAdd(thread_, list, elt3); Object elt2(&scope, runtime_->newStrFromCStr("hello")); runtime_->listAdd(thread_, list, elt2); Object elt1(&scope, runtime_->newStrFromCStr("hello")); runtime_->listAdd(thread_, list, elt1); ASSERT_EQ(listSort(thread_, list), NoneType::object()); EXPECT_EQ(list.numItems(), 4); EXPECT_EQ(list.at(0), *elt2); EXPECT_EQ(list.at(1), *elt1); EXPECT_EQ(list.at(2), *elt4); EXPECT_EQ(list.at(3), *elt3); } TEST_F(ListBuiltinsTest, ListExtendSelfDuplicatesElements) { ASSERT_FALSE(runFromCStr(runtime_, R"( a = [1, 2, 3] a.extend(a) )") .isError()); HandleScope scope(thread_); List a(&scope, mainModuleAt(runtime_, "a")); ASSERT_EQ(a.numItems(), 6); EXPECT_PYLIST_EQ(a, {1, 2, 3, 1, 2, 3}); } TEST_F(ListBuiltinsTest, ListExtendListSubclassFallsBackToIter) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C(list): def __iter__(self): return [4, 5, 6].__iter__() a = [1, 2, 3] a.extend(C([1,2,3])) )") .isError()); HandleScope scope(thread_); List a(&scope, mainModuleAt(runtime_, "a")); ASSERT_EQ(a.numItems(), 6); EXPECT_PYLIST_EQ(a, {1, 2, 3, 4, 5, 6}); } TEST_F(ListBuiltinsTest, ReverseEmptyListDoesNothing) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = [] result.reverse() )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); ASSERT_TRUE(result.isList()); EXPECT_EQ(List::cast(*result).numItems(), 0); } TEST_F(ListBuiltinsTest, ReverseOneElementListDoesNothing) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = [2] result.reverse() )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); ASSERT_TRUE(result.isList()); EXPECT_EQ(List::cast(*result).numItems(), 1); EXPECT_EQ(List::cast(*result).at(0), SmallInt::fromWord(2)); } TEST_F(ListBuiltinsTest, ReverseOddManyElementListReversesList) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = [1, 2, 3] result.reverse() )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_PYLIST_EQ(result, {3, 2, 1}); } TEST_F(ListBuiltinsTest, ReverseEvenManyElementListReversesList) { ASSERT_FALSE(runFromCStr(runtime_, R"( result = [1, 2, 3, 4] result.reverse() )") .isError()); HandleScope scope(thread_); Object result(&scope, mainModuleAt(runtime_, "result")); EXPECT_PYLIST_EQ(result, {4, 3, 2, 1}); } TEST_F(ListBuiltinsTest, ReverseWithListSubclassDoesNotCallSubclassMethods) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C(list): def __getitem__(self, key): raise Exception("hi") def __setitem__(self, key, val): raise Exception("hi") result = C([1, 2, 3, 4]) result.reverse() )") .isError()); EXPECT_FALSE(Thread::current()->hasPendingException()); } TEST_F(ListBuiltinsTest, SortWithMultiElementListSortsElements) { ASSERT_FALSE(runFromCStr(runtime_, R"( ls = [3, 2, 1] ls.sort() )") .isError()); HandleScope scope(thread_); Object ls(&scope, mainModuleAt(runtime_, "ls")); EXPECT_PYLIST_EQ(ls, {1, 2, 3}); } TEST_F(ListBuiltinsTest, SortWithNonCallableKeyRaisesException) { EXPECT_TRUE(raised(runFromCStr(runtime_, R"( ls = [3, 2, 1] ls.sort(key=5) )"), LayoutId::kTypeError)); ; } TEST_F(ListBuiltinsTest, SortWithKeySortsAccordingToKey) { ASSERT_FALSE(runFromCStr(runtime_, R"( ls = [2, 3, 1] ls.sort(key=lambda x: -x) )") .isError()); HandleScope scope(thread_); Object ls(&scope, mainModuleAt(runtime_, "ls")); EXPECT_PYLIST_EQ(ls, {3, 2, 1}); } TEST_F(ListBuiltinsTest, SortReverseReversesSortedList) { ASSERT_FALSE(runFromCStr(runtime_, R"( ls = [2, 3, 1] ls.sort(reverse=True) )") .isError()); HandleScope scope(thread_); Object ls(&scope, mainModuleAt(runtime_, "ls")); EXPECT_PYLIST_EQ(ls, {3, 2, 1}); } TEST_F(ListBuiltinsTest, ClearRemovesElements) { ASSERT_FALSE(runFromCStr(runtime_, R"( ls = [2, 3, 1] list.clear(ls) )") .isError()); HandleScope scope(thread_); Object ls(&scope, mainModuleAt(runtime_, "ls")); EXPECT_PYLIST_EQ(ls, {}); } TEST_F(ListBuiltinsTest, ClearRemovesAllElements) { ASSERT_FALSE(runFromCStr(runtime_, R"( class C: pass l = [C()] )") .isError()); HandleScope scope(thread_); List list(&scope, mainModuleAt(runtime_, "l")); Object ref_obj(&scope, NoneType::object()); { Object c(&scope, list.at(0)); ref_obj = runtime_->newWeakRef(thread_, c); } WeakRef ref(&scope, *ref_obj); EXPECT_NE(ref.referent(), NoneType::object()); runBuiltin(METH(list, clear), list); runtime_->collectGarbage(); EXPECT_EQ(ref.referent(), NoneType::object()); } } // namespace testing } // namespace py