Merge lp:~sil2100/unity/fix-949448-5.0 into lp:unity

Proposed by Łukasz Zemczak
Status: Superseded
Proposed branch: lp:~sil2100/unity/fix-949448-5.0
Merge into: lp:unity
Diff against target: 701 lines (+664/-0) (has conflicts)
3 files modified
tests/autopilot/autopilot/emulators/bamf.py.OTHER (+411/-0)
tests/autopilot/unity/tests/__init__.py (+225/-0)
tests/autopilot/unity/tests/test_launcher.py (+28/-0)
Conflict adding files to tests/autopilot/autopilot.  Created directory.
Conflict because tests/autopilot/autopilot is not versioned, but has versioned children.  Versioned directory.
Conflict adding files to tests/autopilot/autopilot/emulators.  Created directory.
Conflict because tests/autopilot/autopilot/emulators is not versioned, but has versioned children.  Versioned directory.
Contents conflict in tests/autopilot/autopilot/emulators/bamf.py
Text conflict in tests/autopilot/unity/tests/__init__.py
Text conflict in tests/autopilot/unity/tests/test_launcher.py
To merge this branch: bzr merge lp:~sil2100/unity/fix-949448-5.0
Reviewer Review Type Date Requested Status
Unity Team Pending
Review via email: mp+106978@code.launchpad.net

This proposal has been superseded by a proposal from 2012-05-23.

Commit message

Backported andyrock's fix from lp:unity/6.0 - Unmute launcher.

Description of the change

This branch needs to be tested. I see no other dependency changes, but I cannot get it to work with orca somehow. Could anyone else check it? Maybe I'm doing something wrong.

Original MRQ:

Copy-pasted from the original andyrock's MRQ for 6.0:
https://code.launchpad.net/~andyrock/unity/fix-949448/+merge/102860

== Problem ==
Launcher is silent to screen reader users.

== Fix ==
Fix a couple of regressions. Alan Bell confirmed the fix for the launcher.

== Test ==
Not sure how we could test it.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'launcher/BamfLauncherIcon.cpp'
=== modified file 'launcher/LauncherController.cpp'
=== modified file 'plugins/unityshell/src/unitya11y.cpp'
=== added directory 'tests/autopilot/autopilot'
=== added directory 'tests/autopilot/autopilot/emulators'
=== added file 'tests/autopilot/autopilot/emulators/bamf.py.OTHER'
--- tests/autopilot/autopilot/emulators/bamf.py.OTHER 1970-01-01 00:00:00 +0000
+++ tests/autopilot/autopilot/emulators/bamf.py.OTHER 2012-05-23 10:30:27 +0000
@@ -0,0 +1,411 @@
1# Copyright 2011 Canonical
2# Author: Thomi Richards
3#
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
7
8"Various classes for interacting with BAMF."
9
10import dbus
11import dbus.glib
12import gio
13import gobject
14import os
15from Xlib import display, X, protocol
16from gtk import gdk
17
18from autopilot.emulators.dbus_handler import session_bus
19
20__all__ = [
21 "Bamf",
22 "BamfApplication",
23 "BamfWindow",
24 ]
25
26_BAMF_BUS_NAME = 'org.ayatana.bamf'
27_X_DISPLAY = display.Display()
28
29
30def _filter_user_visible(win):
31 """Filter out non-user-visible objects.
32
33 In some cases the DBus method we need to call hasn't been registered yet,
34 in which case we do the safe thing and return False.
35
36 """
37 try:
38 return win.user_visible
39 except dbus.DBusException:
40 return False
41
42
43class Bamf(object):
44 """High-level class for interacting with Bamf from within a test.
45
46 Use this class to inspect the state of running applications and open
47 windows.
48
49 """
50
51 def __init__(self):
52 matcher_path = '/org/ayatana/bamf/matcher'
53 self.matcher_interface_name = 'org.ayatana.bamf.matcher'
54 self.matcher_proxy = session_bus.get_object(_BAMF_BUS_NAME, matcher_path)
55 self.matcher_interface = dbus.Interface(self.matcher_proxy, self.matcher_interface_name)
56
57 def get_running_applications(self, user_visible_only=True):
58 """Get a list of the currently running applications.
59
60 If user_visible_only is True (the default), only applications
61 visible to the user in the switcher will be returned.
62
63 """
64 apps = [BamfApplication(p) for p in self.matcher_interface.RunningApplications()]
65 if user_visible_only:
66 return filter(_filter_user_visible, apps)
67 return apps
68
69 def get_running_applications_by_desktop_file(self, desktop_file):
70 """Return a list of applications that have the desktop file 'desktop_file'`.
71
72 This method may return an empty list, if no applications
73 are found with the specified desktop file.
74
75 """
76 return [a for a in self.get_running_applications() if a.desktop_file == desktop_file]
77
78 def get_application_by_xid(self, xid):
79 """Return the application that has a child with the requested xid or None."""
80
81 app_path = self.matcher_interface.ApplicationForXid(xid)
82 if len(app_path):
83 return BamfApplication(app_path)
84 return None
85
86 def get_open_windows(self, user_visible_only=True):
87 """Get a list of currently open windows.
88
89 If user_visible_only is True (the default), only applications
90 visible to the user in the switcher will be returned.
91
92 The result is sorted to be in stacking order.
93
94 """
95
96 windows = [BamfWindow(w) for w in self.matcher_interface.WindowStackForMonitor(-1)]
97 if user_visible_only:
98 windows = filter(_filter_user_visible, windows)
99 # Now sort on stacking order.
100 return reversed(windows)
101
102 def get_window_by_xid(self, xid):
103 """Get the BamfWindow that matches the provided 'xid'."""
104 windows = [BamfWindow(w) for w in self.matcher_interface.WindowPaths() if BamfWindow(w).x_id == xid]
105 return windows[0] if windows else None
106
107 def wait_until_application_is_running(self, desktop_file, timeout):
108 """Wait until a given application is running.
109
110 'desktop_file' is the name of the application desktop file.
111 'timeout' is the maximum time to wait, in seconds. If set to
112 something less than 0, this method will wait forever.
113
114 This method returns true once the application is found, or false
115 if the application was not found until the timeout was reached.
116 """
117 desktop_file = os.path.split(desktop_file)[1]
118 # python workaround since you can't assign to variables in the enclosing scope:
119 # see on_timeout_reached below...
120 found_app = [True]
121
122 # maybe the app is running already?
123 if len(self.get_running_applications_by_desktop_file(desktop_file)) == 0:
124 wait_forever = timeout < 0
125 gobject_loop = gobject.MainLoop()
126
127 # No, so define a callback to watch the ViewOpened signal:
128 def on_view_added(bamf_path, name):
129 if bamf_path.split('/')[-1].startswith('application'):
130 app = BamfApplication(bamf_path)
131 if desktop_file == os.path.split(app.desktop_file)[1]:
132 gobject_loop.quit()
133
134 # ...and one for when the user-defined timeout has been reached:
135 def on_timeout_reached():
136 gobject_loop.quit()
137 found_app[0] = False
138 return False
139
140 # need a timeout? if so, connect it:
141 if not wait_forever:
142 gobject.timeout_add(timeout * 1000, on_timeout_reached)
143 # connect signal handler:
144 session_bus.add_signal_receiver(on_view_added, 'ViewOpened')
145 # pump the gobject main loop until either the correct signal is emitted, or the
146 # timeout happens.
147 gobject_loop.run()
148
149 return found_app[0]
150
151 def launch_application(self, desktop_file, files=[], wait=True):
152 """Launch an application by specifying a desktop file.
153
154 `files` is a list of files to pass to the application. Not all apps support this.
155
156 If `wait` is True, this method will block until the application has launched.
157
158 Returns the Gobject process object. if wait is True (the default),
159 this method will not return until an instance of this application
160 appears in the BAMF application list.
161 """
162 if type(files) is not list:
163 raise TypeError("files must be a list.")
164 proc = gio.unix.DesktopAppInfo(desktop_file)
165 proc.launch_uris(files)
166 if wait:
167 self.wait_until_application_is_running(desktop_file, -1)
168 return proc
169
170
171class BamfApplication(object):
172 """Represents an application, with information as returned by Bamf.
173
174 Don't instantiate this class yourself. instead, use the methods as
175 provided by the Bamf class.
176
177 """
178 def __init__(self, bamf_app_path):
179 self.bamf_app_path = bamf_app_path
180 try:
181 self._app_proxy = session_bus.get_object(_BAMF_BUS_NAME, bamf_app_path)
182 self._view_iface = dbus.Interface(self._app_proxy, 'org.ayatana.bamf.view')
183 self._app_iface = dbus.Interface(self._app_proxy, 'org.ayatana.bamf.application')
184 except dbus.DBusException, e:
185 e.message += 'bamf_app_path=%r' % (bamf_app_path)
186 raise
187
188 @property
189 def desktop_file(self):
190 """Get the application desktop file"""
191 return os.path.split(self._app_iface.DesktopFile())[1]
192
193 @property
194 def name(self):
195 """Get the application name.
196
197 Note: This may change according to the current locale. If you want a unique
198 string to match applications against, use the desktop_file instead.
199
200 """
201 return self._view_iface.Name()
202
203 @property
204 def icon(self):
205 """Get the application icon."""
206 return self._view_iface.Icon()
207
208 @property
209 def is_active(self):
210 """Is the application active (i.e.- has keyboard focus)?"""
211 return self._view_iface.IsActive()
212
213 @property
214 def is_urgent(self):
215 """Is the application currently signalling urgency?"""
216 return self._view_iface.IsUrgent()
217
218 @property
219 def user_visible(self):
220 """Is this application visible to the user?
221
222 Some applications (such as the panel) are hidden to the user but will
223 still be returned by bamf.
224
225 """
226 return self._view_iface.UserVisible()
227
228 def get_windows(self):
229 """Get a list of the application windows."""
230 return [BamfWindow(w) for w in self._view_iface.Children()]
231
232 def __repr__(self):
233 return "<BamfApplication '%s'>" % (self.name)
234
235
236class BamfWindow(object):
237 """Represents an application window, as returned by Bamf.
238
239 Don't instantiate this class yourself. Instead, use the appropriate methods
240 in BamfApplication.
241
242 """
243 def __init__(self, window_path):
244 self._bamf_win_path = window_path
245 self._app_proxy = session_bus.get_object(_BAMF_BUS_NAME, window_path)
246 self._window_iface = dbus.Interface(self._app_proxy, 'org.ayatana.bamf.window')
247 self._view_iface = dbus.Interface(self._app_proxy, 'org.ayatana.bamf.view')
248
249 self._xid = int(self._window_iface.GetXid())
250 self._x_root_win = _X_DISPLAY.screen().root
251 self._x_win = _X_DISPLAY.create_resource_object('window', self._xid)
252
253 @property
254 def x_id(self):
255 """Get the X11 Window Id."""
256 return self._xid
257
258 @property
259 def x_win(self):
260 """Get the X11 window object of the underlying window."""
261 return self._x_win
262
263 @property
264 def name(self):
265 """Get the window name.
266
267 Note: This may change according to the current locale. If you want a unique
268 string to match windows against, use the x_id instead.
269
270 """
271 return self._view_iface.Name()
272
273 @property
274 def title(self):
275 """Get the window title.
276
277 This may be different from the application name.
278
279 Note that this may change depending on the current locale.
280
281 """
282 return self._getProperty('_NET_WM_NAME')
283
284 @property
285 def geometry(self):
286 """Get the geometry for this window.
287
288 Returns a tuple containing (x, y, width, height).
289
290 """
291 # FIXME: We need to use the gdk window here to get the real coordinates
292 geometry = self._x_win.get_geometry()
293 origin = gdk.window_foreign_new(self._xid).get_origin()
294 return (origin[0], origin[1], geometry.width, geometry.height)
295
296 @property
297 def is_maximized(self):
298 """Is the window maximized?
299
300 Maximized in this case means both maximized
301 vertically and horizontally. If a window is only maximized in one
302 direction it is not considered maximized.
303
304 """
305 win_state = self._get_window_states()
306 return '_NET_WM_STATE_MAXIMIZED_VERT' in win_state and \
307 '_NET_WM_STATE_MAXIMIZED_HORZ' in win_state
308
309 @property
310 def application(self):
311 """Get the application that owns this window.
312
313 This method may return None if the window does not have an associated
314 application. The 'desktop' window is one such example.
315
316 """
317 # BAMF returns a list of parents since some windows don't have an
318 # associated application. For these windows we return none.
319 parents = self._view_iface.Parents()
320 if parents:
321 return BamfApplication(parents[0])
322 else:
323 return None
324
325 @property
326 def user_visible(self):
327 """Is this window visible to the user in the switcher?"""
328 return self._view_iface.UserVisible()
329
330 @property
331 def is_hidden(self):
332 """Is this window hidden?
333
334 Windows are hidden when the 'Show Desktop' mode is activated.
335
336 """
337 win_state = self._get_window_states()
338 return '_NET_WM_STATE_HIDDEN' in win_state
339
340 @property
341 def is_focused(self):
342 """Is this window focused?"""
343 win_state = self._get_window_states()
344 return '_NET_WM_STATE_FOCUSED' in win_state
345
346 @property
347 def is_valid(self):
348 """Is this window object valid?
349
350 Invalid windows are caused by windows closing during the construction of
351 this object instance.
352
353 """
354 return not self._x_win is None
355
356 @property
357 def monitor(self):
358 """Returns the monitor to which the windows belongs to"""
359 return self._window_iface.Monitor()
360
361 @property
362 def closed(self):
363 """Returns True if the window has been closed"""
364 # This will return False when the window is closed and then removed from BUS
365 try:
366 return (self._window_iface.GetXid() != self.x_id)
367 except:
368 return True
369
370 def close(self):
371 """Close the window."""
372
373 self._setProperty('_NET_CLOSE_WINDOW', [0, 0])
374
375 def set_focus(self):
376 self._x_win.set_input_focus(X.RevertToParent, X.CurrentTime)
377 self._x_win.configure(stack_mode=X.Above)
378
379 def __repr__(self):
380 return "<BamfWindow '%s'>" % (self.title if self._x_win else str(self._xid))
381
382 def _getProperty(self, _type):
383 """Get an X11 property.
384
385 _type is a string naming the property type. win is the X11 window object.
386
387 """
388 atom = self._x_win.get_full_property(_X_DISPLAY.get_atom(_type), X.AnyPropertyType)
389 if atom:
390 return atom.value
391
392 def _setProperty(self, _type, data, mask=None):
393 if type(data) is str:
394 dataSize = 8
395 else:
396 # data length must be 5 - pad with 0's if it's short, truncate otherwise.
397 data = (data + [0] * (5 - len(data)))[:5]
398 dataSize = 32
399
400 ev = protocol.event.ClientMessage(window=self._x_win, client_type=_X_DISPLAY.get_atom(_type), data=(dataSize, data))
401
402 if not mask:
403 mask = (X.SubstructureRedirectMask | X.SubstructureNotifyMask)
404 self._x_root_win.send_event(ev, event_mask=mask)
405 _X_DISPLAY.sync()
406
407 def _get_window_states(self):
408 """Return a list of strings representing the current window state."""
409
410 _X_DISPLAY.sync()
411 return map(_X_DISPLAY.get_atom_name, self._getProperty('_NET_WM_STATE'))
0412
=== modified file 'tests/autopilot/unity/emulators/icons.py'
=== modified file 'tests/autopilot/unity/emulators/launcher.py'
=== modified file 'tests/autopilot/unity/tests/__init__.py'
--- tests/autopilot/unity/tests/__init__.py 2012-05-16 23:02:38 +0000
+++ tests/autopilot/unity/tests/__init__.py 2012-05-23 10:30:27 +0000
@@ -122,3 +122,228 @@
122 """122 """
123 set_log_severity(component, level)123 set_log_severity(component, level)
124124
125<<<<<<< TREE
126=======
127
128class VideoCapturedTestCase(LoggedTestCase):
129 """Video capture autopilot tests, saving the results if the test failed."""
130
131 _recording_app = '/usr/bin/recordmydesktop'
132 _recording_opts = ['--no-sound', '--no-frame', '-o',]
133
134 def setUp(self):
135 super(VideoCapturedTestCase, self).setUp()
136 global video_recording_enabled
137 if video_recording_enabled and not self._have_recording_app():
138 video_recording_enabled = False
139 logger.warning("Disabling video capture since '%s' is not present", self._recording_app)
140
141 if video_recording_enabled:
142 self._test_passed = True
143 self.addOnException(self._on_test_failed)
144 self.addCleanup(self._stop_video_capture)
145 self._start_video_capture()
146
147 def _have_recording_app(self):
148 return os.path.exists(self._recording_app)
149
150 def _start_video_capture(self):
151 args = self._get_capture_command_line()
152 self._capture_file = self._get_capture_output_file()
153 self._ensure_directory_exists_but_not_file(self._capture_file)
154 args.append(self._capture_file)
155 logger.debug("Starting: %r", args)
156 self._capture_process = Popen(args, stdout=PIPE, stderr=STDOUT)
157
158 def _stop_video_capture(self):
159 """Stop the video capture. If the test failed, save the resulting file."""
160
161 if self._test_passed:
162 # We use kill here because we don't want the recording app to start
163 # encoding the video file (since we're removing it anyway.)
164 self._capture_process.kill()
165 self._capture_process.wait()
166 else:
167 self._capture_process.terminate()
168 self._capture_process.wait()
169 if self._capture_process.returncode != 0:
170 self.addDetail('video capture log', text_content(self._capture_process.stdout.read()))
171 self._capture_process = None
172
173 def _get_capture_command_line(self):
174 return [self._recording_app] + self._recording_opts
175
176 def _get_capture_output_file(self):
177 return os.path.join(video_record_directory, '%s.ogv' % (self.shortDescription()))
178
179 def _ensure_directory_exists_but_not_file(self, file_path):
180 dirpath = os.path.dirname(file_path)
181 if not os.path.exists(dirpath):
182 os.makedirs(dirpath)
183 elif os.path.exists(file_path):
184 logger.warning("Video capture file '%s' already exists, deleting.", file_path)
185 os.remove(file_path)
186
187 def _on_test_failed(self, ex_info):
188 """Called when a test fails."""
189 self._test_passed = False
190
191
192class AutopilotTestCase(VideoCapturedTestCase, KeybindingsHelper):
193 """Wrapper around testtools.TestCase that takes care of some cleaning."""
194
195 run_test_with = GlibRunner
196
197 KNOWN_APPS = {
198 'Character Map' : {
199 'desktop-file': 'gucharmap.desktop',
200 'process-name': 'gucharmap',
201 },
202 'Calculator' : {
203 'desktop-file': 'gcalctool.desktop',
204 'process-name': 'gcalctool',
205 },
206 'Mahjongg' : {
207 'desktop-file': 'mahjongg.desktop',
208 'process-name': 'mahjongg',
209 },
210 'Remmina' : {
211 'desktop-file': 'remmina.desktop',
212 'process-name': 'remmina',
213 },
214 'System Settings' : {
215 'desktop-file': 'gnome-control-center.desktop',
216 'process-name': 'gnome-control-center',
217 },
218 'Text Editor' : {
219 'desktop-file': 'gedit.desktop',
220 'process-name': 'gedit',
221 },
222 }
223
224 def setUp(self):
225 super(AutopilotTestCase, self).setUp()
226 self.bamf = Bamf()
227 self.keyboard = Keyboard()
228 self.mouse = Mouse()
229 self.dash = Dash()
230 self.hud = Hud()
231 self.launcher = self._get_launcher_controller()
232 self.panels = self._get_panel_controller()
233 self.switcher = Switcher()
234 self.window_manager = self._get_window_manager()
235 self.workspace = WorkspaceManager()
236 self.screen_geo = ScreenGeometry()
237 self.addCleanup(self.workspace.switch_to, self.workspace.current_workspace)
238 self.addCleanup(Keyboard.cleanup)
239 self.addCleanup(Mouse.cleanup)
240
241 def start_app(self, app_name, files=[], locale=None):
242 """Start one of the known apps, and kill it on tear down.
243
244 If files is specified, start the application with the specified files.
245 If locale is specified, the locale will be set when the application is launched.
246
247 The method returns the BamfApplication instance.
248
249 """
250 if locale:
251 os.putenv("LC_ALL", locale)
252 self.addCleanup(os.unsetenv, "LC_ALL")
253 logger.info("Starting application '%s' with files %r in locale %s", app_name, files, locale)
254 else:
255 logger.info("Starting application '%s' with files %r", app_name, files)
256
257 app = self.KNOWN_APPS[app_name]
258 self.bamf.launch_application(app['desktop-file'], files)
259 apps = self.bamf.get_running_applications_by_desktop_file(app['desktop-file'])
260 self.addCleanup(call, "kill `pidof %s`" % (app['process-name']), shell=True)
261 self.assertThat(len(apps), Equals(1))
262 return apps[0]
263
264 def close_all_app(self, app_name):
265 """Close all instances of the app_name."""
266 app = self.KNOWN_APPS[app_name]
267 pids = check_output(["pidof", app['process-name']]).split()
268 if len(pids):
269 call(["kill"] + pids)
270
271 def get_app_instances(self, app_name):
272 """Get BamfApplication instances for app_name."""
273 desktop_file = self.KNOWN_APPS[app_name]['desktop-file']
274 return self.bamf.get_running_applications_by_desktop_file(desktop_file)
275
276 def app_is_running(self, app_name):
277 """Returns true if an instance of the application is running."""
278 apps = self.get_app_instances(app_name)
279 return len(apps) > 0
280
281 def call_gsettings_cmd(self, command, schema, *args):
282 """Set a desktop wide gsettings option
283
284 Using the gsettings command because there's a bug with importing
285 from gobject introspection and pygtk2 simultaneously, and the Xlib
286 keyboard layout bits are very unweildy. This seems like the best
287 solution, even a little bit brutish.
288 """
289 cmd = ['gsettings', command, schema] + list(args)
290 # strip to remove the trailing \n.
291 ret = check_output(cmd).strip()
292 time.sleep(5)
293 reset_display()
294 return ret
295
296 def set_unity_option(self, option_name, option_value):
297 """Set an option in the unity compiz plugin options.
298
299 The value will be set for the current test only.
300
301 """
302 self.set_compiz_option("unityshell", option_name, option_value)
303
304 def set_compiz_option(self, plugin_name, setting_name, setting_value):
305 """Set setting `setting_name` in compiz plugin `plugin_name` to value `setting_value`
306 for one test only.
307 """
308 old_value = self._set_compiz_option(plugin_name, setting_name, setting_value)
309 self.addCleanup(self._set_compiz_option, plugin_name, setting_name, old_value)
310 # Allow unity time to respond to the new setting.
311 time.sleep(0.5)
312
313 def _set_compiz_option(self, plugin_name, option_name, option_value):
314 logger.info("Setting compiz option '%s' in plugin '%s' to %r",
315 option_name, plugin_name, option_value)
316 plugin = Plugin(global_context, plugin_name)
317 setting = Setting(plugin, option_name)
318 old_value = setting.Value
319 setting.Value = option_value
320 global_context.Write()
321 return old_value
322
323 def _get_launcher_controller(self):
324 controllers = LauncherController.get_all_instances()
325 self.assertThat(len(controllers), Equals(1))
326 return controllers[0]
327
328 def _get_panel_controller(self):
329 controllers = PanelController.get_all_instances()
330 self.assertThat(len(controllers), Equals(1))
331 return controllers[0]
332
333 def _get_window_manager(self):
334 managers = WindowManager.get_all_instances()
335 self.assertThat(len(managers), Equals(1))
336 return managers[0]
337
338 def assertVisibleWindowStack(self, stack_start):
339 """Check that the visible window stack starts with the windows passed in.
340
341 The start_stack is an iterable of BamfWindow objects.
342 Minimised windows are skipped.
343
344 """
345 stack = [win for win in self.bamf.get_open_windows() if not win.is_hidden]
346 for pos, win in enumerate(stack_start):
347 self.assertThat(stack[pos].x_id, Equals(win.x_id),
348 "%r at %d does not equal %r" % (stack[pos], pos, win))
349>>>>>>> MERGE-SOURCE
125350
=== modified file 'tests/autopilot/unity/tests/test_launcher.py'
--- tests/autopilot/unity/tests/test_launcher.py 2012-05-17 22:49:33 +0000
+++ tests/autopilot/unity/tests/test_launcher.py 2012-05-23 10:30:27 +0000
@@ -660,6 +660,7 @@
660 logger.info("Checking for duplicated launcher icon for application %s", test_app.name)660 logger.info("Checking for duplicated launcher icon for application %s", test_app.name)
661 self.assertOnlyOneLauncherIcon(desktop_id=test_app.desktop_file)661 self.assertOnlyOneLauncherIcon(desktop_id=test_app.desktop_file)
662662
663<<<<<<< TREE
663 def test_killing_bamfdaemon_does_not_duplicate_application_xids(self):664 def test_killing_bamfdaemon_does_not_duplicate_application_xids(self):
664 """Killing bamfdaemon should not duplicate any xid in the model."""665 """Killing bamfdaemon should not duplicate any xid in the model."""
665 self.start_test_apps()666 self.start_test_apps()
@@ -685,6 +686,33 @@
685686
686687
687class LauncherCaptureTests(UnityTestCase):688class LauncherCaptureTests(UnityTestCase):
689=======
690 def test_killing_bamfdaemon_does_not_duplicate_application_xids(self):
691 """Killing bamfdaemon should not duplicate any xid in the model."""
692 self.start_test_apps()
693 self.start_desktopless_test_apps()
694 self.kill_and_restart_bamfdaemon()
695
696 test_apps = self.get_test_apps() + self.get_desktopless_test_apps()
697
698 for test_app in test_apps:
699 logger.info("Checking for duplicated launcher icon for application %s", test_app.name)
700 test_windows = [w.x_id for w in test_app.get_windows()]
701 self.assertOnlyOneLauncherIcon(xids=test_windows)
702
703 def test_killing_bamfdaemon_does_not_duplicate_any_icon_application_id(self):
704 """Killing bamfdaemon should not duplicate any application ids in the model."""
705 self.start_test_apps()
706 self.start_desktopless_test_apps()
707 self.kill_and_restart_bamfdaemon()
708
709 for icon in self.launcher.model.get_bamf_launcher_icons():
710 logger.info("Checking for duplicated launcher icon %s", icon.tooltip_text)
711 self.assertOnlyOneLauncherIcon(application_id=icon.application_id)
712
713
714class LauncherCaptureTests(AutopilotTestCase):
715>>>>>>> MERGE-SOURCE
688 """Test the launchers ability to capture/not capture the mouse."""716 """Test the launchers ability to capture/not capture the mouse."""
689717
690 screen_geo = ScreenGeometry()718 screen_geo = ScreenGeometry()