# Copyright 2023 (c) Anna Schumaker. """Tests our custom Python-based ListStore.""" import unittest import emmental.store from gi.repository import GObject from gi.repository import Gio class Object(GObject.GObject): """A simple object for testing.""" value = GObject.Property(type=int) class TestListStore(unittest.TestCase): """Test case for the Python-based ListStore.""" def items_changed(self, store: emmental.store.ListStore, pos: int, removed: int, added: int) -> None: """Handle the items-changed signal.""" self.changed = (pos, removed, added) def setUp(self): """Set up common variables.""" self.store = emmental.store.ListStore() self.store.connect("items-changed", self.items_changed) self.changed = (None, None, None) def test_init(self): """Check that the ListStore is initialized properly.""" self.assertIsInstance(self.store, GObject.GObject) self.assertIsInstance(self.store, Gio.ListModel) self.assertListEqual(self.store.items, []) self.assertEqual(self.store.n_items, 0) def test_get_item_type(self): """Test getting the item type for the ListStore.""" self.assertEqual(self.store.get_item_type(), GObject.GObject.__gtype__) def test_get_item(self): """Test accessing items in the ListStore.""" objs = [Object(value=i) for i in range(5)] self.store.extend(objs) for i, obj in enumerate(objs): with self.subTest(i=i): self.assertEqual(self.store.get_item(i), obj) self.assertEqual(self.store[i], obj) self.assertIsNone(self.store.get_item(6)) def test_append(self): """Test the ListStore's append() function.""" for i in [0, 1, 2]: with self.subTest(i=i): self.assertTrue(self.store.append(Object(value=i))) self.assertTupleEqual(self.changed, (i, 0, 1)) self.assertListEqual([obj.value for obj in self.store.items], [v for v in range(i + 1)]) def test_clear(self): """Test clearing a ListStore.""" self.store.clear() self.assertTupleEqual(self.changed, (None, None, None)) self.store.extend([Object(value=i) for i in range(5)]) self.store.clear() self.assertTupleEqual(self.changed, (0, 5, 0)) def test_extend(self): """Test appending multiple values to the ListStore.""" self.store.extend([]) self.assertTupleEqual(self.changed, (None, None, None)) self.assertListEqual(self.store.items, []) self.store.extend([Object(value=0), Object(value=1)]) self.assertTupleEqual(self.changed, (0, 0, 2)) self.assertListEqual([obj.value for obj in self.store.items], [0, 1]) self.store.extend([Object(value=2), Object(value=3), Object(value=4)]) self.assertTupleEqual(self.changed, (2, 0, 3)) self.assertListEqual([obj.value for obj in self.store.items], [0, 1, 2, 3, 4]) def test_index(self): """Test finding the index of items in the ListStore.""" objs = [Object(value=i) for i in range(5)] self.store.extend(objs) for i, obj in enumerate(objs): with self.subTest(i=i): self.assertEqual(self.store.index(obj), i) self.assertTrue(obj in self.store) self.assertIsNone(self.store.index(Object(value=10))) self.assertFalse(Object(value=10) in self.store) def test_insert(self): """Test the ListStore's insert() function.""" self.assertTrue(self.store.insert(0, Object(value=0))) self.assertTupleEqual(self.changed, (0, 0, 1)) self.assertListEqual([obj.value for obj in self.store.items], [0]) self.assertTrue(self.store.insert(0, Object(value=1))) self.assertTupleEqual(self.changed, (0, 0, 1)) self.assertListEqual([obj.value for obj in self.store.items], [1, 0]) self.assertTrue(self.store.insert(5, Object(value=5))) self.assertTupleEqual(self.changed, (2, 0, 1)) self.assertListEqual([obj.value for obj in self.store.items], [1, 0, 5]) self.assertTrue(self.store.insert(-3, Object(value=3))) self.assertTupleEqual(self.changed, (0, 0, 1)) self.assertListEqual([obj.value for obj in self.store.items], [3, 1, 0, 5]) def test_length(self): """Test requesting the ListStore's length.""" for n in [0, 1, 2]: with self.subTest(n=n): self.assertEqual(self.store.get_n_items(), n) self.assertEqual(self.store.n_items, n) self.assertEqual(len(self.store), n) self.store.append(Object(value=n)) def test_pop(self): """Test popping objects from the ListStore.""" self.assertIsNone(self.store.pop(0)) self.assertTupleEqual(self.changed, (None, None, None)) objs = [Object(value=v) for v in range(3)] self.store.extend(objs) self.changed = (None, None, None) self.assertIsNone(self.store.pop(5)) self.assertTupleEqual(self.changed, (None, None, None)) self.assertEqual(self.store.pop(0), objs[0]) self.assertTupleEqual(self.changed, (0, 1, 0)) self.assertListEqual([obj.value for obj in self.store.items], [1, 2]) def test_remove(self): """Test removing objects from the ListStore.""" objs = [Object(value=v) for v in range(3)] self.store.extend(objs) self.assertTrue(self.store.remove(objs[0])) self.assertEqual(self.changed, (0, 1, 0)) self.changed = (None, None, None) self.assertFalse(self.store.remove(objs[0])) self.assertTupleEqual(self.changed, (None, None, None)) class TestSortedList(unittest.TestCase): """Test case for our SortedList implementation.""" def items_changed(self, store: emmental.store.SortedList, pos: int, removed: int, added: int) -> None: """Handle the items-changed signal.""" self.changed = (pos, removed, added) def key_func(self, obj: Object) -> int: """Get a sort key for Objects.""" return obj.value def setUp(self): """Set up common variables.""" self.store = emmental.store.SortedList(self.key_func) self.store.connect("items-changed", self.items_changed) self.changed = (None, None, None) def test_init(self): """Check that the SortedList is initialized properly.""" self.assertIsInstance(self.store, emmental.store.ListStore) self.assertEqual(self.store.key_func, self.key_func) def test_append(self): """Test the SortedList's append() function.""" self.assertTrue(self.store.append(Object(value=2))) self.assertTupleEqual(self.changed, (0, 0, 1)) self.assertListEqual([obj.value for obj in self.store.items], [2]) self.assertTrue(self.store.append(Object(value=0))) self.assertTupleEqual(self.changed, (0, 0, 1)) self.assertListEqual([obj.value for obj in self.store.items], [0, 2]) self.assertTrue(self.store.append(Object(value=1))) self.assertTupleEqual(self.changed, (1, 0, 1)) self.assertListEqual([obj.value for obj in self.store.items], [0, 1, 2]) self.changed = (None, None, None) for value in [0, 1, 2]: with self.subTest(value=value): self.assertFalse(self.store.append(Object(value=value))) self.assertTupleEqual(self.changed, (None, None, None)) self.assertListEqual([obj.value for obj in self.store.items], [0, 1, 2]) def test_extend(self): """Test adding multiple values to the SortedList.""" self.store.extend([]) self.assertTupleEqual(self.changed, (None, None, None)) self.assertListEqual(self.store.items, []) self.store.extend([Object(value=1), Object(value=3)]) self.assertTupleEqual(self.changed, (0, 0, 2)) self.assertListEqual([obj.value for obj in self.store.items], [1, 3]) self.store.extend([Object(value=0), Object(value=2), Object(value=4)]) self.assertTupleEqual(self.changed, (0, 2, 5)) self.assertListEqual([obj.value for obj in self.store.items], [0, 1, 2, 3, 4]) def test_index(self): """Test finding the index of items in the SortedList.""" objs = [Object(value=i) for i in range(5)] self.store.extend(objs) for i, obj in enumerate(objs): with self.subTest(i=i): self.assertEqual(self.store.index(obj), i) self.assertIsNone(self.store.index(Object(value=10))) def test_insert(self): """Test the SortedList's insert() function.""" self.assertTrue(self.store.insert(2, Object(value=2))) self.assertTupleEqual(self.changed, (0, 0, 1)) self.assertListEqual([obj.value for obj in self.store.items], [2]) self.assertTrue(self.store.insert(1, Object(value=0))) self.assertTupleEqual(self.changed, (0, 0, 1)) self.assertListEqual([obj.value for obj in self.store.items], [0, 2]) self.assertTrue(self.store.insert(0, Object(value=1))) self.assertTupleEqual(self.changed, (1, 0, 1)) self.assertListEqual([obj.value for obj in self.store.items], [0, 1, 2]) self.changed = (None, None, None) for i, obj in enumerate(self.store): with self.subTest(i=i): self.assertFalse(self.store.insert(0, obj)) self.assertTupleEqual(self.changed, (None, None, None)) self.assertListEqual([obj.value for obj in self.store.items], [0, 1, 2])