From 99445d825580512b1c3cebcbbd17ae378df5c29b Mon Sep 17 00:00:00 2001 From: daksh Date: Sun, 22 Mar 2026 23:58:55 +0530 Subject: [PATCH 1/2] Replace GObject.GObject() with super().__init__() --- extensions/cpsection/keyboard/view.py | 2 +- extensions/deviceicon/audio.py | 2 +- extensions/deviceicon/battery.py | 2 +- src/jarabe/desktop/grid.py | 2 +- src/jarabe/desktop/meshbox.py | 2 +- src/jarabe/frame/clipboard.py | 2 +- src/jarabe/frame/eventarea.py | 2 +- src/jarabe/journal/iconmodel.py | 2 +- src/jarabe/journal/listmodel.py | 2 +- src/jarabe/journal/palettes.py | 2 +- src/jarabe/model/adhoc.py | 2 +- src/jarabe/model/brightness.py | 2 +- src/jarabe/model/buddy.py | 2 +- src/jarabe/model/bundleregistry.py | 2 +- src/jarabe/model/desktop.py | 2 +- src/jarabe/model/filetransfer.py | 2 +- src/jarabe/model/friends.py | 2 +- src/jarabe/model/invites.py | 2 +- src/jarabe/model/neighborhood.py | 6 +- src/jarabe/model/network.py | 4 +- src/jarabe/model/session.py | 2 +- src/jarabe/model/shell.py | 2 +- src/jarabe/model/update/aslo.py | 2 +- src/jarabe/model/update/microformat.py | 2 +- src/jarabe/model/update/updater.py | 2 +- src/jarabe/util/downloader.py | 2 +- .../util/telepathy/connection_watcher.py | 2 +- tests/test_gobject_super.py | 256 ++++++++++++++++++ 28 files changed, 286 insertions(+), 30 deletions(-) create mode 100644 tests/test_gobject_super.py diff --git a/extensions/cpsection/keyboard/view.py b/extensions/cpsection/keyboard/view.py index f3c2722123..e31a49f7fd 100644 --- a/extensions/cpsection/keyboard/view.py +++ b/extensions/cpsection/keyboard/view.py @@ -75,7 +75,7 @@ class LayoutCombo(Gtk.Box): } def __init__(self, keyboard_manager, n): - GObject.GObject.__init__(self) + super().__init__() self._keyboard_manager = keyboard_manager self._index = n diff --git a/extensions/deviceicon/audio.py b/extensions/deviceicon/audio.py index e1b7c12487..60bbf37d12 100644 --- a/extensions/deviceicon/audio.py +++ b/extensions/deviceicon/audio.py @@ -245,7 +245,7 @@ class DeviceModelAudio(GObject.GObject): } def __init__(self): - GObject.GObject.__init__(self) + super().__init__() self._device = None self._last_level = 0 diff --git a/extensions/deviceicon/battery.py b/extensions/deviceicon/battery.py index 32ee4f15fb..a88087914d 100644 --- a/extensions/deviceicon/battery.py +++ b/extensions/deviceicon/battery.py @@ -197,7 +197,7 @@ class DeviceModel(GObject.GObject): } def __init__(self, battery): - GObject.GObject.__init__(self) + super().__init__() self._battery = UPowerGlib.Device() self._battery.set_object_path_sync(battery, None) self._connect_battery() diff --git a/src/jarabe/desktop/grid.py b/src/jarabe/desktop/grid.py index 48b32d4eed..614bb7e74d 100644 --- a/src/jarabe/desktop/grid.py +++ b/src/jarabe/desktop/grid.py @@ -36,7 +36,7 @@ class Grid(SugarExt.Grid): } def __init__(self, width, height): - GObject.GObject.__init__(self) + super().__init__() self._children = [] self._child_rects = {} diff --git a/src/jarabe/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py index 9c73f80941..dde01b1794 100644 --- a/src/jarabe/desktop/meshbox.py +++ b/src/jarabe/desktop/meshbox.py @@ -180,7 +180,7 @@ class DeviceObserver(GObject.GObject): } def __init__(self, device): - GObject.GObject.__init__(self) + super().__init__() self._bus = dbus.SystemBus() self.device = device diff --git a/src/jarabe/frame/clipboard.py b/src/jarabe/frame/clipboard.py index 315aece02f..15dcb2eff8 100644 --- a/src/jarabe/frame/clipboard.py +++ b/src/jarabe/frame/clipboard.py @@ -45,7 +45,7 @@ class Clipboard(GObject.GObject): } def __init__(self): - GObject.GObject.__init__(self) + super().__init__() self._objects = {} self._next_id = 0 diff --git a/src/jarabe/frame/eventarea.py b/src/jarabe/frame/eventarea.py index 35d78cdce6..97efed31d4 100644 --- a/src/jarabe/frame/eventarea.py +++ b/src/jarabe/frame/eventarea.py @@ -36,7 +36,7 @@ class EventArea(GObject.GObject): } def __init__(self, settings): - GObject.GObject.__init__(self) + super().__init__() self._hover = False self._sids = {} diff --git a/src/jarabe/journal/iconmodel.py b/src/jarabe/journal/iconmodel.py index 713b89c09d..e4608d07bb 100644 --- a/src/jarabe/journal/iconmodel.py +++ b/src/jarabe/journal/iconmodel.py @@ -46,7 +46,7 @@ class IconModel(GObject.GObject, Gtk.TreeModel, Gtk.TreeDragSource): _PAGE_SIZE = 100 def __init__(self, query): - GObject.GObject.__init__(self) + super().__init__() self._last_requested_index = None self._cached_row = None diff --git a/src/jarabe/journal/listmodel.py b/src/jarabe/journal/listmodel.py index cb13a39018..cb0962d301 100644 --- a/src/jarabe/journal/listmodel.py +++ b/src/jarabe/journal/listmodel.py @@ -75,7 +75,7 @@ class ListModel(GObject.GObject, Gtk.TreeModel, Gtk.TreeDragSource): _PAGE_SIZE = 10 def __init__(self, query): - GObject.GObject.__init__(self) + super().__init__() self._last_requested_index = None self._temp_drag_file_uid = None diff --git a/src/jarabe/journal/palettes.py b/src/jarabe/journal/palettes.py index feb82e4e40..97a6625268 100644 --- a/src/jarabe/journal/palettes.py +++ b/src/jarabe/journal/palettes.py @@ -579,7 +579,7 @@ def __init__(self, journalactivity, uid_list, alert_title, alert_message, operation_cb): - GObject.GObject.__init__(self) + super().__init__() self._journalactivity = journalactivity diff --git a/src/jarabe/model/adhoc.py b/src/jarabe/model/adhoc.py index 78ca8d1ed2..e27dd7a04f 100644 --- a/src/jarabe/model/adhoc.py +++ b/src/jarabe/model/adhoc.py @@ -57,7 +57,7 @@ class AdHocManager(GObject.GObject): _CHANNEL_11 = 11 def __init__(self): - GObject.GObject.__init__(self) + super().__init__() self._bus = dbus.SystemBus() self._device = None diff --git a/src/jarabe/model/brightness.py b/src/jarabe/model/brightness.py index e046d4f1b4..a05ac21ad5 100644 --- a/src/jarabe/model/brightness.py +++ b/src/jarabe/model/brightness.py @@ -39,7 +39,7 @@ class Brightness(GObject.GObject): changed_signal = GObject.Signal('changed', arg_types=([int])) def __init__(self): - GObject.GObject.__init__(self) + super().__init__() self._path = None self._helper_path = None self._max_brightness = None diff --git a/src/jarabe/model/buddy.py b/src/jarabe/model/buddy.py index 6ae3299a9f..05dde2489c 100644 --- a/src/jarabe/model/buddy.py +++ b/src/jarabe/model/buddy.py @@ -43,7 +43,7 @@ def __init__(self, **kwargs): self._tags = None self._current_activity = None - GObject.GObject.__init__(self, **kwargs) + super().__init__(**kwargs) def get_nick(self): return self._nick diff --git a/src/jarabe/model/bundleregistry.py b/src/jarabe/model/bundleregistry.py index 57bd7ec358..dfdfa8093d 100644 --- a/src/jarabe/model/bundleregistry.py +++ b/src/jarabe/model/bundleregistry.py @@ -72,7 +72,7 @@ class BundleRegistry(GObject.GObject): def __init__(self): logging.debug('STARTUP: Loading the bundle registry') - GObject.GObject.__init__(self) + super().__init__() self._mime_defaults = self._load_mime_defaults() diff --git a/src/jarabe/model/desktop.py b/src/jarabe/model/desktop.py index 47c912bc4d..ed75bd88cc 100644 --- a/src/jarabe/model/desktop.py +++ b/src/jarabe/model/desktop.py @@ -33,7 +33,7 @@ class DesktopViewModel(GObject.GObject): } def __init__(self): - GObject.GObject.__init__(self) + super().__init__() self._number_of_views = 1 self._view_icons = None diff --git a/src/jarabe/model/filetransfer.py b/src/jarabe/model/filetransfer.py index aef04007de..84b4a29445 100644 --- a/src/jarabe/model/filetransfer.py +++ b/src/jarabe/model/filetransfer.py @@ -64,7 +64,7 @@ class BaseFileTransfer(GObject.GObject): def __init__(self, connection): - GObject.GObject.__init__(self) + super().__init__() self._connection = connection self._state = FT_STATE_NONE self._transferred_bytes = 0 diff --git a/src/jarabe/model/friends.py b/src/jarabe/model/friends.py index 6cc48aa32e..c5dc0464d6 100644 --- a/src/jarabe/model/friends.py +++ b/src/jarabe/model/friends.py @@ -105,7 +105,7 @@ class Friends(GObject.GObject): } def __init__(self): - GObject.GObject.__init__(self) + super().__init__() self._friends = {} self._path = os.path.join(env.get_profile_path(), 'friends') diff --git a/src/jarabe/model/invites.py b/src/jarabe/model/invites.py index a8f3d52ce5..bd9e70c45b 100644 --- a/src/jarabe/model/invites.py +++ b/src/jarabe/model/invites.py @@ -161,7 +161,7 @@ class Invites(GObject.GObject): } def __init__(self): - GObject.GObject.__init__(self) + super().__init__() self._dispatch_operations = {} diff --git a/src/jarabe/model/neighborhood.py b/src/jarabe/model/neighborhood.py index fd8f4da8c5..39fabbfdf6 100644 --- a/src/jarabe/model/neighborhood.py +++ b/src/jarabe/model/neighborhood.py @@ -87,7 +87,7 @@ class ActivityModel(GObject.GObject): } def __init__(self, activity_id, room_handle): - GObject.GObject.__init__(self) + super().__init__() self.activity_id = activity_id self.room_handle = room_handle @@ -188,7 +188,7 @@ class _Account(GObject.GObject): } def __init__(self, account_path): - GObject.GObject.__init__(self) + super().__init__() self.object_path = account_path @@ -754,7 +754,7 @@ class Neighborhood(GObject.GObject): } def __init__(self): - GObject.GObject.__init__(self) + super().__init__() self._buddies = {None: get_owner_instance()} self._activities = {} diff --git a/src/jarabe/model/network.py b/src/jarabe/model/network.py index 6e8227bf32..9495a73fb8 100644 --- a/src/jarabe/model/network.py +++ b/src/jarabe/model/network.py @@ -639,7 +639,7 @@ class AccessPoint(GObject.GObject): } def __init__(self, device, model): - GObject.GObject.__init__(self) + super().__init__() self.device = device self.model = model @@ -787,7 +787,7 @@ class Connection(GObject.GObject): } def __init__(self, bus, path): - GObject.GObject.__init__(self) + super().__init__() obj = bus.get_object(NM_SERVICE, path) self._connection = dbus.Interface(obj, NM_CONNECTION_IFACE) self._removed_handle = self._connection.connect_to_signal( diff --git a/src/jarabe/model/session.py b/src/jarabe/model/session.py index 175655dac7..67ad0e208e 100644 --- a/src/jarabe/model/session.py +++ b/src/jarabe/model/session.py @@ -43,7 +43,7 @@ class SessionManager(GObject.GObject): MAX_SHUTDOWN_TRIES = 10 def __init__(self): - GObject.GObject.__init__(self) + super().__init__() #address = SugarExt.xsmp_init() #os.environ['SESSION_MANAGER'] = address diff --git a/src/jarabe/model/shell.py b/src/jarabe/model/shell.py index a0571cf97b..3c70cd442d 100644 --- a/src/jarabe/model/shell.py +++ b/src/jarabe/model/shell.py @@ -72,7 +72,7 @@ def __init__(self, activity_info, activity_id, color, window=None): _windows -- GtkWindows registered for the activity. The lowest one in the stack is the main window. """ - GObject.GObject.__init__(self) + super().__init__() self._windows = [] self._service = None diff --git a/src/jarabe/model/update/aslo.py b/src/jarabe/model/update/aslo.py index d741e03ed4..26bb4dc1c8 100644 --- a/src/jarabe/model/update/aslo.py +++ b/src/jarabe/model/update/aslo.py @@ -78,7 +78,7 @@ class _UpdateChecker(GObject.GObject): _CHUNK_SIZE = 10240 def __init__(self): - GObject.GObject.__init__(self) + super().__init__() self._bundle = None def check(self, bundle): diff --git a/src/jarabe/model/update/microformat.py b/src/jarabe/model/update/microformat.py index 5786c214af..b9bcb1d9fe 100644 --- a/src/jarabe/model/update/microformat.py +++ b/src/jarabe/model/update/microformat.py @@ -356,7 +356,7 @@ class MetadataLookup(GObject.GObject): } def __init__(self, url): - GObject.GObject.__init__(self) + super().__init__() self._url = url self._icon_file_name = None self._size = None diff --git a/src/jarabe/model/update/updater.py b/src/jarabe/model/update/updater.py index b43eb26b56..99381af7b8 100644 --- a/src/jarabe/model/update/updater.py +++ b/src/jarabe/model/update/updater.py @@ -66,7 +66,7 @@ class Updater(GObject.GObject): } def __init__(self): - GObject.GObject.__init__(self) + super().__init__() settings = Gio.Settings.new(_UPDATE_KEYS_PATH) backend = settings.get_string(_UPDATE_BACKEND_KEY) diff --git a/src/jarabe/util/downloader.py b/src/jarabe/util/downloader.py index e5ebd75af7..d1f5843031 100644 --- a/src/jarabe/util/downloader.py +++ b/src/jarabe/util/downloader.py @@ -62,7 +62,7 @@ class Downloader(GObject.GObject): } def __init__(self, url, session=None, request_headers=None): - GObject.GObject.__init__(self) + super().__init__() self._uri = GLib.Uri.parse(url, GLib.UriFlags.NONE) self._session = session or get_soup_session() self._pending_buffers = [] diff --git a/src/jarabe/util/telepathy/connection_watcher.py b/src/jarabe/util/telepathy/connection_watcher.py index b579402bd1..295d72900d 100644 --- a/src/jarabe/util/telepathy/connection_watcher.py +++ b/src/jarabe/util/telepathy/connection_watcher.py @@ -45,7 +45,7 @@ class ConnectionWatcher(GObject.GObject): } def __init__(self, bus=None): - GObject.GObject.__init__(self) + super().__init__() if bus is None: self.bus = dbus.Bus() diff --git a/tests/test_gobject_super.py b/tests/test_gobject_super.py new file mode 100644 index 0000000000..319215bc08 --- /dev/null +++ b/tests/test_gobject_super.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025, Sugar Labs +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +"""Test GObject super() initialization pattern. + +This test file demonstrates how to verify that GObject subclasses +are properly initialized when using super().__init__() instead of +GObject.GObject().__init__(self). +""" + +import unittest +from gi.repository import GObject, GLib + + +class SimpleGObjectClass(GObject.GObject): + """Example class with super() replacement.""" + + __gsignals__ = { + 'test-signal': (GObject.SignalFlags.RUN_FIRST, None, (object,)), + } + + def __init__(self): + super().__init__() # This replaced: GObject.GObject.__init__(self) + self.test_prop = "initialized" + + +class GObjectWithKwargs(GObject.GObject): + """GObject class with **kwargs pattern (like BaseBuddyModel).""" + + test_prop = GObject.Property(type=str, default="") + + def __init__(self, **kwargs): + self._internal_prop = None + super().__init__(**kwargs) # This replaced: GObject.GObject.__init__(self, **kwargs) + + +class TestGObjectSuperPattern(unittest.TestCase): + """Verify super().__init__() properly initializes GObject.""" + + def test_instantiation_works(self): + """Test that class can be instantiated.""" + obj = SimpleGObjectClass() + self.assertIsNotNone(obj) + + def test_is_gobject_subclass(self): + """Verify it's a proper GObject subclass.""" + obj = SimpleGObjectClass() + self.assertIsInstance(obj, GObject.GObject) + + def test_has_gobject_methods(self): + """Verify GObject methods are available.""" + obj = SimpleGObjectClass() + # Essential GObject methods + self.assertTrue(hasattr(obj, 'emit')) + self.assertTrue(hasattr(obj, 'connect')) + self.assertTrue(hasattr(obj, 'freeze_notify')) + self.assertTrue(hasattr(obj, 'thaw_notify')) + + def test_properties_initialized(self): + """Test instance properties are set correctly.""" + obj = SimpleGObjectClass() + self.assertEqual(obj.test_prop, "initialized") + + def test_signal_emission(self): + """Test signal emission works with super() init.""" + obj = SimpleGObjectClass() + signals_received = [] + + def on_signal(obj, data): + signals_received.append(data) + + obj.connect('test-signal', on_signal) + obj.emit('test-signal', 'test_value') + + self.assertEqual(len(signals_received), 1) + self.assertEqual(signals_received[0], 'test_value') + + def test_multiple_signal_handlers(self): + """Test multiple handlers work correctly.""" + obj = SimpleGObjectClass() + results = [] + + def handler1(obj, data): + results.append(('handler1', data)) + + def handler2(obj, data): + results.append(('handler2', data)) + + obj.connect('test-signal', handler1) + obj.connect('test-signal', handler2) + obj.emit('test-signal', 'msg') + + self.assertEqual(len(results), 2) + self.assertIn(('handler1', 'msg'), results) + self.assertIn(('handler2', 'msg'), results) + + def test_freeze_thaw_notify(self): + """Test freeze/thaw notify works.""" + obj = SimpleGObjectClass() + # These should not raise exceptions + obj.freeze_notify() + obj.thaw_notify() + + def test_multiple_instances(self): + """Test creating multiple independent instances.""" + obj1 = SimpleGObjectClass() + obj2 = SimpleGObjectClass() + + obj1.test_prop = "obj1_value" + obj2.test_prop = "obj2_value" + + self.assertNotEqual(obj1.test_prop, obj2.test_prop) + self.assertIsNot(obj1, obj2) + + def test_class_with_initialization_chain(self): + """Test complex initialization chain.""" + init_order = [] + + class Parent(GObject.GObject): + def __init__(self): + super().__init__() + init_order.append('parent') + + class Child(Parent): + def __init__(self): + super().__init__() # Calls Parent.__init__ + init_order.append('child') + + obj = Child() + # Verify initialization happened in correct order + self.assertIn('parent', init_order) + self.assertIn('child', init_order) + self.assertIsInstance(obj, GObject.GObject) + + +class TestSuperVsOldPattern(unittest.TestCase): + """Compare new super() pattern with old GObject.GObject() pattern.""" + + def test_both_patterns_create_valid_gobjects(self): + """Verify both patterns result in valid GObjects.""" + # New pattern (using super()) + new_obj = SimpleGObjectClass() + + self.assertIsInstance(new_obj, GObject.GObject) + self.assertTrue(hasattr(new_obj, 'emit')) + self.assertTrue(hasattr(new_obj, 'connect')) + + def test_signal_emission_consistency(self): + """Test that signals work the same way.""" + obj = SimpleGObjectClass() + emitted = [] + + obj.connect('test-signal', lambda o, d: emitted.append(d)) + obj.emit('test-signal', 'data') + + self.assertEqual(emitted, ['data']) + + def test_property_assignment_works(self): + """Verify property assignment works correctly.""" + obj = SimpleGObjectClass() + obj.test_prop = "new_value" + self.assertEqual(obj.test_prop, "new_value") + + +class TestGObjectInitializationVerification(unittest.TestCase): + """Tests to verify GObject was properly initialized.""" + + def test_glib_main_context_available(self): + """Verify GLib integration works.""" + obj = SimpleGObjectClass() + context = GLib.MainContext.default() + self.assertIsNotNone(context) + + def test_signal_not_firing_before_connection(self): + """Verify signal doesn't fire if not connected.""" + obj = SimpleGObjectClass() + handler_called = [] + + # Connect after emission + obj.emit('test-signal', 'data1') + obj.connect('test-signal', lambda o, d: handler_called.append(d)) + obj.emit('test-signal', 'data2') + + # Only data2 should be in the list + self.assertEqual(handler_called, ['data2']) + + def test_reference_counting_works(self): + """Verify GObject reference counting is functional.""" + obj = SimpleGObjectClass() + # GObject uses reference counting + # Create a reference and verify it exists + ref = obj + self.assertIs(ref, obj) + + +class TestSuperWithKwargs(unittest.TestCase): + """Test super().__init__(**kwargs) pattern as used in BaseBuddyModel.""" + + def test_kwargs_instantiation(self): + """Test class with **kwargs can be instantiated.""" + obj = GObjectWithKwargs() + self.assertIsNotNone(obj) + + def test_kwargs_is_gobject_subclass(self): + """Verify kwargs class is a proper GObject subclass.""" + obj = GObjectWithKwargs() + self.assertIsInstance(obj, GObject.GObject) + + def test_kwargs_with_properties(self): + """Test **kwargs pattern with GObject properties.""" + obj = GObjectWithKwargs(test_prop="custom_value") + self.assertEqual(obj.props.test_prop, "custom_value") + + def test_kwargs_without_properties(self): + """Test **kwargs without passing properties.""" + obj = GObjectWithKwargs() + self.assertEqual(obj.props.test_prop, "") + + def test_kwargs_internal_properties_initialized(self): + """Verify internal properties are initialized before super().""" + obj = GObjectWithKwargs() + self.assertIsNone(obj._internal_prop) + + def test_kwargs_multiple_instances(self): + """Test multiple instances with different property values.""" + obj1 = GObjectWithKwargs(test_prop="value1") + obj2 = GObjectWithKwargs(test_prop="value2") + + self.assertEqual(obj1.props.test_prop, "value1") + self.assertEqual(obj2.props.test_prop, "value2") + self.assertIsNot(obj1, obj2) + + def test_kwargs_has_gobject_methods(self): + """Verify GObject methods work with kwargs pattern.""" + obj = GObjectWithKwargs() + self.assertTrue(hasattr(obj, 'emit')) + self.assertTrue(hasattr(obj, 'connect')) + self.assertTrue(hasattr(obj, 'freeze_notify')) + self.assertTrue(hasattr(obj, 'thaw_notify')) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From f45ac818214e0c40f6db0c0ffc896efc79bdca0d Mon Sep 17 00:00:00 2001 From: daksh Date: Tue, 24 Mar 2026 20:19:28 +0530 Subject: [PATCH 2/2] Removed the test --- tests/test_gobject_super.py | 256 ------------------------------------ 1 file changed, 256 deletions(-) delete mode 100644 tests/test_gobject_super.py diff --git a/tests/test_gobject_super.py b/tests/test_gobject_super.py deleted file mode 100644 index 319215bc08..0000000000 --- a/tests/test_gobject_super.py +++ /dev/null @@ -1,256 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (C) 2025, Sugar Labs -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -"""Test GObject super() initialization pattern. - -This test file demonstrates how to verify that GObject subclasses -are properly initialized when using super().__init__() instead of -GObject.GObject().__init__(self). -""" - -import unittest -from gi.repository import GObject, GLib - - -class SimpleGObjectClass(GObject.GObject): - """Example class with super() replacement.""" - - __gsignals__ = { - 'test-signal': (GObject.SignalFlags.RUN_FIRST, None, (object,)), - } - - def __init__(self): - super().__init__() # This replaced: GObject.GObject.__init__(self) - self.test_prop = "initialized" - - -class GObjectWithKwargs(GObject.GObject): - """GObject class with **kwargs pattern (like BaseBuddyModel).""" - - test_prop = GObject.Property(type=str, default="") - - def __init__(self, **kwargs): - self._internal_prop = None - super().__init__(**kwargs) # This replaced: GObject.GObject.__init__(self, **kwargs) - - -class TestGObjectSuperPattern(unittest.TestCase): - """Verify super().__init__() properly initializes GObject.""" - - def test_instantiation_works(self): - """Test that class can be instantiated.""" - obj = SimpleGObjectClass() - self.assertIsNotNone(obj) - - def test_is_gobject_subclass(self): - """Verify it's a proper GObject subclass.""" - obj = SimpleGObjectClass() - self.assertIsInstance(obj, GObject.GObject) - - def test_has_gobject_methods(self): - """Verify GObject methods are available.""" - obj = SimpleGObjectClass() - # Essential GObject methods - self.assertTrue(hasattr(obj, 'emit')) - self.assertTrue(hasattr(obj, 'connect')) - self.assertTrue(hasattr(obj, 'freeze_notify')) - self.assertTrue(hasattr(obj, 'thaw_notify')) - - def test_properties_initialized(self): - """Test instance properties are set correctly.""" - obj = SimpleGObjectClass() - self.assertEqual(obj.test_prop, "initialized") - - def test_signal_emission(self): - """Test signal emission works with super() init.""" - obj = SimpleGObjectClass() - signals_received = [] - - def on_signal(obj, data): - signals_received.append(data) - - obj.connect('test-signal', on_signal) - obj.emit('test-signal', 'test_value') - - self.assertEqual(len(signals_received), 1) - self.assertEqual(signals_received[0], 'test_value') - - def test_multiple_signal_handlers(self): - """Test multiple handlers work correctly.""" - obj = SimpleGObjectClass() - results = [] - - def handler1(obj, data): - results.append(('handler1', data)) - - def handler2(obj, data): - results.append(('handler2', data)) - - obj.connect('test-signal', handler1) - obj.connect('test-signal', handler2) - obj.emit('test-signal', 'msg') - - self.assertEqual(len(results), 2) - self.assertIn(('handler1', 'msg'), results) - self.assertIn(('handler2', 'msg'), results) - - def test_freeze_thaw_notify(self): - """Test freeze/thaw notify works.""" - obj = SimpleGObjectClass() - # These should not raise exceptions - obj.freeze_notify() - obj.thaw_notify() - - def test_multiple_instances(self): - """Test creating multiple independent instances.""" - obj1 = SimpleGObjectClass() - obj2 = SimpleGObjectClass() - - obj1.test_prop = "obj1_value" - obj2.test_prop = "obj2_value" - - self.assertNotEqual(obj1.test_prop, obj2.test_prop) - self.assertIsNot(obj1, obj2) - - def test_class_with_initialization_chain(self): - """Test complex initialization chain.""" - init_order = [] - - class Parent(GObject.GObject): - def __init__(self): - super().__init__() - init_order.append('parent') - - class Child(Parent): - def __init__(self): - super().__init__() # Calls Parent.__init__ - init_order.append('child') - - obj = Child() - # Verify initialization happened in correct order - self.assertIn('parent', init_order) - self.assertIn('child', init_order) - self.assertIsInstance(obj, GObject.GObject) - - -class TestSuperVsOldPattern(unittest.TestCase): - """Compare new super() pattern with old GObject.GObject() pattern.""" - - def test_both_patterns_create_valid_gobjects(self): - """Verify both patterns result in valid GObjects.""" - # New pattern (using super()) - new_obj = SimpleGObjectClass() - - self.assertIsInstance(new_obj, GObject.GObject) - self.assertTrue(hasattr(new_obj, 'emit')) - self.assertTrue(hasattr(new_obj, 'connect')) - - def test_signal_emission_consistency(self): - """Test that signals work the same way.""" - obj = SimpleGObjectClass() - emitted = [] - - obj.connect('test-signal', lambda o, d: emitted.append(d)) - obj.emit('test-signal', 'data') - - self.assertEqual(emitted, ['data']) - - def test_property_assignment_works(self): - """Verify property assignment works correctly.""" - obj = SimpleGObjectClass() - obj.test_prop = "new_value" - self.assertEqual(obj.test_prop, "new_value") - - -class TestGObjectInitializationVerification(unittest.TestCase): - """Tests to verify GObject was properly initialized.""" - - def test_glib_main_context_available(self): - """Verify GLib integration works.""" - obj = SimpleGObjectClass() - context = GLib.MainContext.default() - self.assertIsNotNone(context) - - def test_signal_not_firing_before_connection(self): - """Verify signal doesn't fire if not connected.""" - obj = SimpleGObjectClass() - handler_called = [] - - # Connect after emission - obj.emit('test-signal', 'data1') - obj.connect('test-signal', lambda o, d: handler_called.append(d)) - obj.emit('test-signal', 'data2') - - # Only data2 should be in the list - self.assertEqual(handler_called, ['data2']) - - def test_reference_counting_works(self): - """Verify GObject reference counting is functional.""" - obj = SimpleGObjectClass() - # GObject uses reference counting - # Create a reference and verify it exists - ref = obj - self.assertIs(ref, obj) - - -class TestSuperWithKwargs(unittest.TestCase): - """Test super().__init__(**kwargs) pattern as used in BaseBuddyModel.""" - - def test_kwargs_instantiation(self): - """Test class with **kwargs can be instantiated.""" - obj = GObjectWithKwargs() - self.assertIsNotNone(obj) - - def test_kwargs_is_gobject_subclass(self): - """Verify kwargs class is a proper GObject subclass.""" - obj = GObjectWithKwargs() - self.assertIsInstance(obj, GObject.GObject) - - def test_kwargs_with_properties(self): - """Test **kwargs pattern with GObject properties.""" - obj = GObjectWithKwargs(test_prop="custom_value") - self.assertEqual(obj.props.test_prop, "custom_value") - - def test_kwargs_without_properties(self): - """Test **kwargs without passing properties.""" - obj = GObjectWithKwargs() - self.assertEqual(obj.props.test_prop, "") - - def test_kwargs_internal_properties_initialized(self): - """Verify internal properties are initialized before super().""" - obj = GObjectWithKwargs() - self.assertIsNone(obj._internal_prop) - - def test_kwargs_multiple_instances(self): - """Test multiple instances with different property values.""" - obj1 = GObjectWithKwargs(test_prop="value1") - obj2 = GObjectWithKwargs(test_prop="value2") - - self.assertEqual(obj1.props.test_prop, "value1") - self.assertEqual(obj2.props.test_prop, "value2") - self.assertIsNot(obj1, obj2) - - def test_kwargs_has_gobject_methods(self): - """Verify GObject methods work with kwargs pattern.""" - obj = GObjectWithKwargs() - self.assertTrue(hasattr(obj, 'emit')) - self.assertTrue(hasattr(obj, 'connect')) - self.assertTrue(hasattr(obj, 'freeze_notify')) - self.assertTrue(hasattr(obj, 'thaw_notify')) - - -if __name__ == '__main__': - unittest.main() \ No newline at end of file