Merge lp:~sinzui/bzr-gtk/notify-gtk-import into lp:bzr-gtk

Proposed by Curtis Hovey on 2012-02-03
Status: Merged
Approved by: Curtis Hovey on 2012-02-04
Approved revision: 776
Merge reported by: Curtis Hovey
Merged at revision: not available
Proposed branch: lp:~sinzui/bzr-gtk/notify-gtk-import
Merge into: lp:bzr-gtk
Diff against target: 232 lines (+99/-21)
5 files modified
bzr-notify (+24/-14)
notify.py (+9/-7)
preferences/plugins.py (+3/-0)
tests/__init__.py (+1/-0)
tests/test_notify.py (+62/-0)
To merge this branch: bzr merge lp:~sinzui/bzr-gtk/notify-gtk-import
Reviewer Review Type Date Requested Status
Jelmer Vernooij (community) 2012-02-03 Approve on 2012-02-04
Review via email: mp+91497@code.launchpad.net

Description of the change

Update bzr-notify to Gtk3 and AppIndicator3.

    Launchpad bug: https://bugs.launchpad.net/bugs/903444
    Pre-implementation: no one

I see a stacktrace that indicates that bzr-notify is importing gtk2 libs.
    $ bzr-notify
    Traceback (most recent call last):
      File "/usr/bin/bzr-notify", line 20, in <module>
        import gobject
      File "/usr/lib/python2.7/dist-packages/gobject/__init__.py", line 47...
        from gobject.constants import *
      File "/usr/lib/python2.7/dist-packages/gobject/constants.py", line 23...

--------------------------------------------------------------------

RULES

    * Update the imports to use gir.
    * Addendum
      * pynotify and appindicator needs updating to gir.
        The precise bzr-gtk package appears to recommend
        gir1.2-appindicator3-0.1 | gir1.2-notify-0.7
        but I do not think they were actually used.

QA

    Using Unity/appindicator3-0
    * Switch your .bazaar/plugins/gtk to point to this branch.
    * Run bzr-notify from the tree
    * Commit a change to a test branch
    * Verify the notification appears
    * Verify the indicator icon appears in the panel
    * Quickly open the bzr-gtk preferences from the indicator menu.
    * When the indicator disappears, close the preferences window
    * Verify there is not an error in the about a bad path:
      p = bzrlib.plugin.plugins()[self.model[path][0]].module

    I tested Notify and instrumenting an import failure of AppIndicator3.
    The notification appeared, but my I did not see the notification icon
    appear. There were no errors in the terminal. I added bzr-notify to
    the dconf notification area exceptions, but that still did not let
    the icon appear.

IMPLEMENTATION

Fixed a race condition where a cursor change event is fired while the
widget is being destroyed. I discovered this while watching the console
when I closed the preferences window.
  preferences/plugins.py

Updated the modules to import from GObject, Notify, and AppIndicator3.
The test for bzr-notify is crude. It will report of the process fails to
start because of import or system errors, but cannot test the actual
function. We need to move the two menu implementations and the message
implementation to another module to test them.
  bzr-notify
  notify.py
  tests/__init__.py
  tests/test_notify.py

To post a comment you must log in.
lp:~sinzui/bzr-gtk/notify-gtk-import updated on 2012-02-03
776. By Curtis Hovey on 2012-02-03

Use the one-true way to setup the plugin path for the test.

Jelmer Vernooij (jelmer) wrote :

Nice, and great to have some smoke tests.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzr-notify'
2--- bzr-notify 2011-07-31 16:50:29 +0000
3+++ bzr-notify 2012-02-03 23:06:20 +0000
4@@ -15,31 +15,36 @@
5 Gtk = open_display()
6
7 import cgi
8+import sys
9+
10 import dbus
11 import dbus.service
12-import gobject
13-import pynotify
14+from gi.repository import GObject
15+from gi.repository import Notify
16+
17 from bzrlib.bzrdir import BzrDir
18 from bzrlib.osutils import format_date
19 from bzrlib.transport import get_transport
20
21 menu = NotifyPopupMenu()
22 try:
23- import appindicator
24+ from gi.repository import AppIndicator3
25 except ImportError:
26- icon = gtk.status_icon_new_from_icon_name("bzr-panel")
27+ icon = Gtk.StatusIcon.new_from_icon_name("bzr-panel")
28 icon.connect('popup-menu', menu.display)
29 icon.set_visible(False)
30 hide_icon = lambda: icon.set_visible(False)
31 show_icon = lambda: icon.set_visible(True)
32 else:
33- indicator = appindicator.Indicator ("bzr-gtk-notify",
34- "bzr-panel", appindicator.CATEGORY_OTHER)
35- indicator.set_status (appindicator.STATUS_PASSIVE)
36+ indicator = AppIndicator3.Indicator.new(
37+ "bzr-gtk-notify", "bzr-panel", AppIndicator3.IndicatorCategory.OTHER)
38+ indicator.set_status (AppIndicator3.IndicatorStatus.PASSIVE)
39 indicator.set_attention_icon("bzr-panel")
40 indicator.set_menu(menu)
41- hide_icon = lambda: indicator.set_status (appindicator.STATUS_PASSIVE)
42- show_icon = lambda: indicator.set_status (appindicator.STATUS_ATTENTION)
43+ hide_icon = lambda: indicator.set_status (
44+ AppIndicator3.IndicatorStatus.PASSIVE)
45+ show_icon = lambda: indicator.set_status (
46+ AppIndicator3.IndicatorStatus.ATTENTION)
47
48 if getattr(dbus, 'version', (0,0,0)) >= (0,41,0):
49 import dbus.glib
50@@ -64,7 +69,7 @@
51 body += '\n'
52 body += revision.message
53 body = cgi.escape(body)
54- nw = pynotify.Notification(summary, body)
55+ nw = Notify.Notification.new(summary, body, None)
56 def start_viz(notification=None, action=None, data=None):
57 """Start the viz program."""
58 from bzrlib.plugins.gtk.commands import start_viz_window
59@@ -75,11 +80,11 @@
60 from bzrlib.plugins.gtk.branch import BranchDialog
61 bd = BranchDialog(remote_path=url)
62 bd.run()
63- if "actions" in pynotify.get_server_caps():
64+ if "actions" in Notify.get_server_caps():
65 nw.add_action("inspect", "Inspect", start_viz, None)
66 nw.add_action("branch", "Branch", start_branch, None)
67 show_icon()
68- gobject.timeout_add(5000, hide_icon)
69+ GObject.timeout_add(5000, hide_icon)
70 nw.set_timeout(5000)
71 nw.show()
72 except Exception, e:
73@@ -88,5 +93,10 @@
74 bus.add_signal_receiver(catch_branch,
75 dbus_interface=BROADCAST_INTERFACE,
76 signal_name="Revision")
77-pynotify.init("bzr-notify")
78-gtk.main()
79+Notify.init("bzr-notify")
80+
81+if sys.argv[-1] == 'test':
82+ # Exit before main loop.
83+ sys.exit(0)
84+
85+Gtk.main()
86
87=== modified file 'notify.py'
88--- notify.py 2011-09-03 01:25:04 +0000
89+++ notify.py 2012-02-03 23:06:20 +0000
90@@ -30,6 +30,8 @@
91
92 class NotifyPopupMenu(Gtk.Menu):
93
94+ SHOW_WIDGETS = True
95+
96 def __init__(self):
97 super(NotifyPopupMenu, self).__init__()
98 self.create_items()
99@@ -46,7 +48,7 @@
100 except ImportError:
101 item.set_sensitive(False)
102 except errors.BzrError:
103- # FIXME: Should only catch errors that indicate a lan-notify
104+ # FIXME: Should only catch errors that indicate a lan-notify
105 # process is already running.
106 item.set_sensitive(False)
107
108@@ -62,20 +64,21 @@
109 except ImportError:
110 item.set_sensitive(False)
111
112- item = Gtk.ImageMenuItem(Gtk.STOCK_PREFERENCES, None)
113+ item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_PREFERENCES, None)
114 item.connect('activate', self.show_preferences)
115 self.append(item)
116- item = Gtk.ImageMenuItem(Gtk.STOCK_ABOUT, None)
117+ item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_ABOUT, None)
118 item.connect('activate', self.show_about)
119 self.append(item)
120 self.append(Gtk.SeparatorMenuItem())
121- item = Gtk.ImageMenuItem(Gtk.STOCK_QUIT, None)
122+ item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_QUIT, None)
123 item.connect('activate', Gtk.main_quit)
124 self.append(item)
125- self.show_all()
126+ if self.SHOW_WIDGETS:
127+ self.show_all()
128
129 def display(self, icon, event_button, event_time):
130- self.popup(None, None, Gtk.status_icon_position_menu,
131+ self.popup(None, None, Gtk.status_icon_position_menu,
132 event_button, event_time, icon)
133
134 def toggle_lan_gateway(self, item):
135@@ -99,4 +102,3 @@
136 from bzrlib.plugins.gtk.preferences import PreferencesWindow
137 prefs = PreferencesWindow()
138 prefs.run()
139-
140
141=== modified file 'preferences/plugins.py'
142--- preferences/plugins.py 2011-09-05 03:44:26 +0000
143+++ preferences/plugins.py 2012-02-03 23:06:20 +0000
144@@ -72,6 +72,9 @@
145 def row_selected(self, tv, path=None, tvc=None):
146 if path is None:
147 (path, focus) = tv.get_cursor()
148+ if path is None:
149+ # The event was fired as the widget was destroyed.
150+ return
151 import bzrlib
152 p = bzrlib.plugin.plugins()[self.model[path][0]].module
153 from inspect import getdoc
154
155=== modified file 'tests/__init__.py'
156--- tests/__init__.py 2012-01-23 16:52:40 +0000
157+++ tests/__init__.py 2012-02-03 23:06:20 +0000
158@@ -24,6 +24,7 @@
159 'test_history',
160 'test_graphcell',
161 'test_linegraph',
162+ 'test_notify',
163 'test_revisionview',
164 'test_treemodel',
165 ]
166
167=== added file 'tests/test_notify.py'
168--- tests/test_notify.py 1970-01-01 00:00:00 +0000
169+++ tests/test_notify.py 2012-02-03 23:06:20 +0000
170@@ -0,0 +1,62 @@
171+# -*- coding: utf-8 -*-
172+# Copyright (C) 2012 Curtis C. Hovey <sinzui.is@verizon.net>
173+#
174+# This program is free software; you can redistribute it and/or modify
175+# it under the terms of the GNU General Public License as published by
176+# the Free Software Foundation; either version 2 of the License, or
177+# (at your option) any later version.
178+#
179+# This program is distributed in the hope that it will be useful,
180+# but WITHOUT ANY WARRANTY; without even the implied warranty of
181+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
182+# GNU General Public License for more details.
183+#
184+# You should have received a copy of the GNU General Public License
185+# along with this program; if not, write to the Free Software
186+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
187+
188+import os
189+import subprocess
190+
191+from bzrlib import tests
192+from bzrlib.plugins.gtk.notify import NotifyPopupMenu
193+
194+
195+class FakeNotifyPopupMenu(NotifyPopupMenu):
196+
197+ SHOW_WIDGETS = False
198+
199+
200+class NotifyPopupMenuTestCase(tests.TestCase):
201+
202+ def test_init(self):
203+ menu = FakeNotifyPopupMenu()
204+ items = menu.get_children()
205+ self.assertEqual(8, len(items))
206+ self.assertEqual('_Gateway to LAN', items[0].props.label)
207+ self.assertEqual('Announce _branches on LAN', items[2].props.label)
208+ self.assertEqual('gtk-preferences', items[4].props.label)
209+ self.assertEqual('gtk-about', items[5].props.label)
210+ self.assertEqual('gtk-quit', items[7].props.label)
211+
212+
213+class BzrNotifyTestCase(tests.TestCase):
214+
215+ def setUp(self):
216+ top = os.path.abspath(os.path.join(
217+ os.path.dirname(__file__), os.pardir))
218+ self.script = os.path.join(top, 'bzr-notify')
219+ self.env = dict(os.environ)
220+ self.env['BZR_PLUGINS_AT'] = 'gtk@%s' % top
221+ super(BzrNotifyTestCase, self).setUp()
222+
223+ def test_smoketest(self):
224+ # This is a smoke test to verify the process starts.
225+ # The logic of the module must be moved into notify.py
226+ # where it can be properly tested.
227+ bzr_notify = subprocess.Popen(
228+ [self.script, 'test'],
229+ stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self.env)
230+ stdout, stderr = bzr_notify.communicate()
231+ self.assertEqual('', stdout)
232+ self.assertEqual('', stderr)

Subscribers

People subscribed via source and target branches

to all changes: