Skip to content

Commit 713eb79

Browse files
committed
Add test for native storage refcounting
1 parent 335ffd2 commit 713eb79

File tree

1 file changed

+110
-1
lines changed
  • graalpython/com.oracle.graal.python.test/src/tests

1 file changed

+110
-1
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_list.py

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2024, Oracle and/or its affiliates.
1+
# Copyright (c) 2018, 2025, Oracle and/or its affiliates.
22
# Copyright (C) 1996-2017 Python Software Foundation
33
#
44
# Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -860,5 +860,114 @@ def test_clear(self):
860860
with self.assertRaisesRegex(TypeError, "list"):
861861
list.clear(42)
862862

863+
864+
class TestObject:
865+
def __init__(self, name):
866+
self.name = name
867+
868+
def __repr__(self):
869+
return self.name
870+
871+
872+
class NativeStorageTests(unittest.TestCase):
873+
def setUp(self):
874+
self.o1 = TestObject('o1')
875+
self.o2 = TestObject('o2')
876+
self.o3 = TestObject('o3')
877+
self.o4 = TestObject('o4')
878+
self.list = [self.o1, self.o2, self.o3]
879+
storage_to_native(self.list)
880+
881+
def assert_refcount(self, o, expected_refcount):
882+
expected_refcount += 10 if sys.implementation.name == 'graalpy' else 3
883+
refcount = sys.getrefcount(o)
884+
if refcount == -1 and sys.implementation.name == 'graalpy':
885+
refcount = 10
886+
self.assertEqual(refcount, expected_refcount,
887+
f"Expected {o} to have refcount {expected_refcount}, was {refcount}")
888+
889+
def check_refcounts(self, o1=1, o2=1, o3=1, o4=0):
890+
self.assert_refcount(self.o1, o1)
891+
self.assert_refcount(self.o2, o2)
892+
self.assert_refcount(self.o3, o3)
893+
self.assert_refcount(self.o4, o4)
894+
895+
def test_get(self):
896+
self.assertEqual(self.list[1], self.o2)
897+
self.assertEqual(self.list[-1], self.o3)
898+
self.assertEqual(self.list[1:], [self.o2, self.o3])
899+
900+
def test_set_del(self):
901+
# n.b. it's important to test the setting and deleting operations together to properly test interactions between
902+
# shrinking and then growing the same storage
903+
self.list[1] = self.o4
904+
self.assertEqual(self.list, [self.o1, self.o4, self.o3])
905+
self.check_refcounts(o1=1, o2=0, o3=1, o4=1)
906+
self.list[1:] = [self.o2, self.o3, self.o4]
907+
self.assertEqual(self.list, [self.o1, self.o2, self.o3, self.o4])
908+
self.check_refcounts(o1=1, o2=1, o3=1, o4=1)
909+
del self.list[1]
910+
self.assertEqual(self.list, [self.o1, self.o3, self.o4])
911+
self.check_refcounts(o1=1, o2=0, o3=1, o4=1)
912+
self.list[1:1] = [self.o2]
913+
self.assertEqual(self.list, [self.o1, self.o2, self.o3, self.o4])
914+
self.check_refcounts(o1=1, o2=1, o3=1, o4=1)
915+
del self.list[1:3]
916+
self.assertEqual(self.list, [self.o1, self.o4])
917+
self.check_refcounts(o1=1, o2=0, o3=0, o4=1)
918+
self.list[1:] = [self.o2, self.o3]
919+
self.assertEqual(self.list, [self.o1, self.o2, self.o3])
920+
self.check_refcounts(o1=1, o2=1, o3=1, o4=0)
921+
922+
def test_insert_remove(self):
923+
self.list.remove(self.o3)
924+
self.assertEqual(self.list, [self.o1, self.o2])
925+
self.check_refcounts(o1=1, o2=1, o3=0, o4=0)
926+
self.list.insert(0, self.o4)
927+
self.assertEqual(self.list, [self.o4, self.o1, self.o2])
928+
self.check_refcounts(o1=1, o2=1, o3=0, o4=1)
929+
930+
def test_append_pop(self):
931+
self.list.pop(1)
932+
self.assertEqual(self.list, [self.o1, self.o3])
933+
self.check_refcounts(o1=1, o2=0, o3=1, o4=0)
934+
self.list.append(self.o4)
935+
self.assertEqual(self.list, [self.o1, self.o3, self.o4])
936+
self.check_refcounts(o1=1, o2=0, o3=1, o4=1)
937+
938+
def test_extend(self):
939+
self.list.extend([self.o4, self.o1])
940+
self.assertEqual(self.list, [self.o1, self.o2, self.o3, self.o4, self.o1])
941+
self.check_refcounts(o1=2, o2=1, o3=1, o4=1)
942+
del self.list[1:]
943+
self.assertEqual(self.list, [self.o1])
944+
self.check_refcounts(o1=1, o2=0, o3=0, o4=0)
945+
self.list += [self.o2, self.o3]
946+
self.assertEqual(self.list, [self.o1, self.o2, self.o3])
947+
self.check_refcounts(o1=1, o2=1, o3=1, o4=0)
948+
949+
def test_inplace_repeat(self):
950+
self.list *= 2
951+
self.assertEqual(self.list, [self.o1, self.o2, self.o3, self.o1, self.o2, self.o3])
952+
# We don't actually do it in place, we make a new managed storage
953+
# self.check_refcounts(o1=2, o2=2, o3=2, o4=0)
954+
955+
def test_sort(self):
956+
self.list.sort(key=lambda x: x.name, reverse=True)
957+
self.assertEqual(self.list, [self.o3, self.o2, self.o1])
958+
self.check_refcounts(o1=1, o2=1, o3=1, o4=0)
959+
960+
def test_reverse(self):
961+
self.list.reverse()
962+
self.assertEqual(self.list, [self.o3, self.o2, self.o1])
963+
self.check_refcounts(o1=1, o2=1, o3=1, o4=0)
964+
965+
def test_clear(self):
966+
self.list.clear()
967+
self.assertEqual(self.list, [])
968+
# We create a new storage and let the old be GC'ed
969+
# self.check_refcounts(o1=0, o2=0, o3=0, o4=0)
970+
971+
863972
if __name__ == '__main__':
864973
unittest.main()

0 commit comments

Comments
 (0)