Merge lp:~pitti/autopilot-gtk/add-tests into lp:autopilot-gtk

Proposed by Martin Pitt
Status: Merged
Approved by: Francis Ginther
Approved revision: 70
Merged at revision: 46
Proposed branch: lp:~pitti/autopilot-gtk/add-tests
Merge into: lp:autopilot-gtk
Diff against target: 1081 lines (+946/-61)
12 files modified
CMakeLists.txt (+1/-1)
debian/control (+6/-1)
debian/rules (+3/-2)
tests/autopilot/tests/test_actions.py (+184/-0)
tests/autopilot/tests/test_gnome_system_log.py (+61/-0)
tests/autopilot/tests/test_matching_properties.py (+0/-32)
tests/autopilot/tests/test_properties.py (+99/-0)
tests/autopilot/tests/test_widget_tree.py (+186/-0)
tests/autopilot/tests/test_xpath_query.py (+110/-0)
tests/hello_color.py (+53/-0)
tests/hello_color.ui (+243/-0)
tests/test-matching.sh (+0/-25)
To merge this branch: bzr merge lp:~pitti/autopilot-gtk/add-tests
Reviewer Review Type Date Requested Status
Francis Ginther Approve
PS Jenkins bot continuous-integration Approve
Martin Pitt (community) Needs Resubmitting
Thomi Richards (community) Needs Fixing
Autopilot Hackers fixed license and test robustness Pending
Review via email: mp+171036@code.launchpad.net

Commit message

Add integration test suite (LP: #1083612)

Description of the change

Add some integration tests, using a tiny tests/hello_color.py application.
There is also a test case that exercises gnome-system-log, which is a lot
lighter than gedit. All these supersede the old test_matching_properties.py.
Please see the individual commits for details.

To post a comment you must log in.
Revision history for this message
Martin Pitt (pitti) wrote :

This MP depends on (and contains) the bits for running the tests during package build: https://code.launchpad.net/~pitti/autopilot-gtk/testsuite/+merge/170607 . So please review that one first.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Hi,

It looks to me like your copyright header is malformed - you should have two paragraphs.

Other than that, looks good to me.

review: Needs Fixing
lp:~pitti/autopilot-gtk/add-tests updated
56. By Martin Pitt

add complete GPL copyright headers

Revision history for this message
Martin Pitt (pitti) wrote :

Indeed, I added complete ones now.

review: Needs Resubmitting
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~pitti/autopilot-gtk/add-tests updated
57. By Martin Pitt

Add test case for GtkMenuItem globalRect

This verifies https://bugs.launchpad.net/autopilot-gtk/+bug/1133893

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~pitti/autopilot-gtk/add-tests updated
58. By Martin Pitt

tests/hello_color.py: Implement about dialog

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~pitti/autopilot-gtk/add-tests updated
59. By Martin Pitt

adjust tests after previous commit

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~pitti/autopilot-gtk/add-tests updated
60. By Martin Pitt

Add test case for XPath queries

This also reproduces https://launchpad.net/bugs/1179806

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~pitti/autopilot-gtk/add-tests updated
61. By Martin Pitt

refine test to work with the autopilot runner, too

62. By Martin Pitt

drop tests/test-matching.sh; broken, and superseded by test_xpath_query.py

63. By Martin Pitt

debian/control: Add trailing comma to build dependencies to avoid future patch noise

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Francis Ginther (fginther) wrote :

I've started another CI build to get another sample to see if the same error reproduces. The failure could be caused by a timing difference.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Francis Ginther (fginther) wrote :

More failures this time. I'm concerned that this is a product of running with xvfb. We may have to stop running the tests during package build.

I am working on a solution to running these types of tests during CI, but outside of the build itself. No ETA yet as I've only started.

Revision history for this message
Martin Pitt (pitti) wrote :

I can reproduce this failure on my machine when I load it heavily (I have a VM with an autopkgtest running in the background). Looks like I need an Eventually matcher there, the test currently assumes that the GtkEntry has the typed text right after self.app.type(...).

BTW, I ran the tests with verbosity, and it seems it's that achingly slow not because of xvfb, but because of some timeouts due to the local D-BUS:

1: 06:44:22.605 INFO globals:49 - Starting test tests.test_actions.ActionsTest.test_greeting_keyboard
1: 06:44:23.077 INFO __init__:136 - Launching process: ['/home/martin/upstream/ap-gtk-tests/tests/hello_color.py']
1: 06:44:23.082 INFO __init__:169 - Looking for autopilot interface for PID 11949 (and children)
1: 06:44:23.103 WARNING __init__:186 - Caught exception while searching for autopilot interface: 'DBusException("Could not get PID of name 'org.freedesktop.DBus': no such name",)'
1: 06:44:24.131 WARNING __init__:186 - Caught exception while searching for autopilot interface: 'DBusException("Could not get PID of name 'org.freedesktop.DBus': no such name",)'
1: 06:44:49.157 ERROR proxies:410 - Introspect error on :1.7:/com/canonical/Autopilot/Introspection: dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NoReply: Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.
1: 06:44:49.157 DEBUG proxies:413 - Executing introspect queue due to error
1: 06:45:14.180 WARNING __init__:186 - Caught exception while searching for autopilot interface: 'DBusException('Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.',)'
1: 06:45:14.192 DEBUG dbus:354 - Selecting objects of type GtkEntry with attributes: {}

So it spends almost a minute(!) on each test case on these. I'll try to track that down, too.

lp:~pitti/autopilot-gtk/add-tests updated
64. By Martin Pitt

merge with trunk

65. By Martin Pitt

Run tests with verbosity, to see more detailled output in build logs on failures

66. By Martin Pitt

Fix d-bus startup during package build

Drop our own dbus-launch invocation in debian/rules; that instance fights with
at-spi's own D-BUS launcher, causing long timeouts in autopilot. Also, that
instance never got cleaned up.

This drops the time to run the tests from 10.5 minutes to 47 seconds due to
avoiding the D-BUS timeouts.

67. By Martin Pitt

Fix test_actions.test_menu

In xvfb, the menu might actually start at (0,0), cover this (literal) corner
case.

68. By Martin Pitt

Work around default focus problem in Xvfb

When running under Xvfb, there is sometimes no default focus. If the entry_name
doesn't have focus, click it to work around this.

69. By Martin Pitt

Use Eventually matchers for robustness

Revision history for this message
Martin Pitt (pitti) wrote :

I made some progress:

 * r66 fixes the abysmally long test run time due to the aforementioned D-BUS timeouts. It now runs in ~ 45 seconds instead of 10.5 minutes.

 * r68 works around the test program in xvfb sometimes not having the default focus. That's what caused the above "Joe" != "" failures. Debugging this properly is a bit tricky, I wanted to find out which widget has the default focus (if at all), but I found that select_many() doesn't work for int/bool properties. I'm investigating this now, will file a bug, and add a test case. I marked these as "FIXME" for now, but the workaround is IMHO not too bad. Also, it will become more general and easier to spot after the fix for bug 1082391 lands.

 * The other are various smaller improvements, see their changelogs.

Now, let's see how the CI run likes these :-) I ran this in sbuild about six times and it always passed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Martin Pitt (pitti) wrote :

OK, two passes in a row, and armhf tests only take 77.51 sec. Feel free to trigger a third build, but from my POV this is robust enough now (I ran this through sbuild another three times).

lp:~pitti/autopilot-gtk/add-tests updated
70. By Martin Pitt

Add (failing) test cases for selecting int/bool properties

This reproduces https://launchpad.net/bugs/1194763

Revision history for this message
Francis Ginther (fginther) wrote :

So I had some trouble running the tests on raring outside of the package build (i.e. "cmake .; make; make test"). But it all ran fine under saucy. The failures are in:

tests.test_actions.ActionsTest.test_menu
 - The test attempts to open the menu where it would appear if it were embedded in the app window, but my menu is in the unity panel.

tests.test_gnome_system_log.GnomeAppTest.test_search
 - This test fails because it can't find the 'GdRevealer'. Possibly this doesn't exist in the saucy version of gnome-system-log?

Given that the failures are not seen on saucy (where development is targeted). I don't see a reason not to approve.

review: Approve
Revision history for this message
Martin Pitt (pitti) wrote :

Indeed in saucy some programs fell back to having integrated menus even under Unity, such as gtimelog or the hello_color.py test application. I'm not sure whether that's deliberate or a bug, I'll ask Didier later. But it should be possible to force a builtin menu with setting UBUNTU_MENUPROXY.

The test_search test should then just be skipped if gnome-system-log is too old.

I'll try this stuff in a raring VM (I don't currently have a raring installation), and do another MP to fix the package build on raring.

Revision history for this message
Martin Pitt (pitti) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2013-06-25 14:42:17 +0000
3+++ CMakeLists.txt 2013-06-26 09:35:33 +0000
4@@ -11,4 +11,4 @@
5 add_subdirectory(lib)
6
7 enable_testing()
8-add_test(nose sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/tests/autopilot; GTK_PATH=${CMAKE_CURRENT_BINARY_DIR}/lib autopilot run tests")
9+add_test(nose sh -c "cd ${CMAKE_CURRENT_SOURCE_DIR}/tests/autopilot; GTK_PATH=${CMAKE_CURRENT_BINARY_DIR}/lib autopilot run -v tests")
10
11=== modified file 'debian/control'
12--- debian/control 2013-06-20 13:50:10 +0000
13+++ debian/control 2013-06-26 09:35:33 +0000
14@@ -11,7 +11,12 @@
15 xvfb,
16 dbus-x11,
17 python-autopilot,
18- gedit
19+ python-xlib,
20+ python-evdev,
21+ python-gi,
22+ gir1.2-gtk-3.0,
23+ gsettings-desktop-schemas,
24+ gnome-system-log,
25 Standards-Version: 3.9.4
26 Section: libs
27 Homepage: https://launchpad.net/autopilot-gtk
28
29=== modified file 'debian/rules'
30--- debian/rules 2013-06-20 13:50:10 +0000
31+++ debian/rules 2013-06-26 09:35:33 +0000
32@@ -7,8 +7,9 @@
33 dh $@
34
35 override_dh_auto_test:
36- mkdir -p debian/tmp/home
37- env HOME=debian/tmp/home xvfb-run dbus-launch dh_auto_test
38+ mkdir -p debian/tmp/home/run
39+ env HOME=$(CURDIR)/debian/tmp/home XDG_RUNTIME_DIR=$(CURDIR)/debian/tmp/home/run \
40+ xvfb-run dh_auto_test
41
42 override_dh_install:
43 dh_install --fail-missing
44
45=== added file 'tests/autopilot/tests/test_actions.py'
46--- tests/autopilot/tests/test_actions.py 1970-01-01 00:00:00 +0000
47+++ tests/autopilot/tests/test_actions.py 2013-06-26 09:35:33 +0000
48@@ -0,0 +1,184 @@
49+# blackbox testing of autopilot API against our hello_color.py test GTK program
50+# Author: Martin Pitt <martin.pitt@ubuntu.com>
51+# Copyright (C) 2013 Canonical Ltd
52+#
53+# This program is free software: you can redistribute it and/or modify
54+# it under the terms of the GNU General Public License version 3 as
55+# published by the Free Software Foundation.
56+#
57+# This program is distributed in the hope that it will be useful,
58+# but WITHOUT ANY WARRANTY; without even the implied warranty of
59+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
60+# GNU General Public License for more details.
61+#
62+# You should have received a copy of the GNU General Public License
63+# along with this program. If not, see <http://www.gnu.org/licenses/>.
64+
65+import os.path
66+
67+from autopilot.testcase import AutopilotTestCase
68+from autopilot.matchers import Eventually
69+from testtools.matchers import Equals, NotEquals
70+
71+tests_dir = os.path.dirname(os.path.dirname(os.path.dirname(
72+ os.path.realpath(__file__))))
73+test_app = os.path.join(tests_dir, 'hello_color.py')
74+
75+
76+class ActionsTest(AutopilotTestCase):
77+ """Test performing actions in the UI and verify results"""
78+
79+ def setUp(self):
80+ super(ActionsTest, self).setUp()
81+ self.app = self.launch_test_application(test_app, app_type='gtk')
82+
83+ def test_greeting_keyboard(self):
84+ """Greeting with keyboard navigation"""
85+
86+ entries = self.app.select_many('GtkEntry')
87+ self.assertEqual(len(entries), 2)
88+ # the upper entry is for the name, the lower for the color
89+ # FIXME: once we have proper names (LP# 1082391), replace this with an
90+ # assertion
91+ if entries[0].globalRect[1] < entries[1].globalRect[1]:
92+ (entry_name, entry_color) = entries
93+ else:
94+ (entry_color, entry_name) = entries
95+
96+ # FIXME: This isn't necessary for real X, but under Xvfb there is no
97+ # default focus sometimes
98+ if not entry_name.has_focus:
99+ self.mouse.click_object(entry_name)
100+
101+ # type in name and color
102+ self.keyboard.type('Joe')
103+ self.keyboard.press_and_release('Tab')
104+ self.keyboard.type('red')
105+
106+ # entries should now have the typed text
107+ self.assertThat(entry_name.text, Eventually(Equals('Joe')))
108+ self.assertThat(entry_color.text, Eventually(Equals('red')))
109+
110+ # should not have any dialogs
111+ self.assertEqual(self.app.select_single('GtkMessageDialog'), None)
112+
113+ # focus and activate the "Greet" button
114+ self.keyboard.press_and_release('Tab')
115+ self.keyboard.press_and_release('Enter')
116+
117+ # should get the greeting dialog
118+ self.assertThat(lambda: self.app.select_single('GtkMessageDialog', visible=True),
119+ Eventually(NotEquals(None)))
120+ md = self.app.select_single('GtkMessageDialog')
121+
122+ # we expect the message dialog to show the corresponding greeting
123+ self.assertNotEqual(md.select_single('GtkLabel',
124+ label=u'Hello Joe, you like red.'),
125+ None)
126+
127+ # close the dialog
128+ self.keyboard.press_and_release('Enter')
129+ self.assertThat(lambda: self.app.select_single('GtkMessageDialog', visible=True),
130+ Eventually(Equals(None)))
131+
132+ def test_greeting_mouse(self):
133+ """Greeting with mouse navigation"""
134+
135+ entries = self.app.select_many('GtkEntry')
136+ self.assertEqual(len(entries), 2)
137+ # the upper entry is for the name, the lower for the color
138+ # FIXME: once we have proper names (LP# 1082391), replace this with an
139+ # assertion
140+ if entries[0].globalRect[1] < entries[1].globalRect[1]:
141+ (entry_name, entry_color) = entries
142+ else:
143+ (entry_color, entry_name) = entries
144+
145+ # FIXME: This isn't necessary for real X, but under Xvfb there is no
146+ # default focus sometimes
147+ if not entry_name.has_focus:
148+ self.mouse.click_object(entry_name)
149+
150+ # type in name and color
151+ self.keyboard.type('Joe')
152+ self.mouse.click_object(entry_color)
153+ self.keyboard.type('blue')
154+
155+ # entries should now have the typed text
156+ self.assertThat(entry_name.text, Eventually(Equals('Joe')))
157+ self.assertThat(entry_color.text, Eventually(Equals('blue')))
158+
159+ # should not have any dialogs
160+ self.assertEqual(self.app.select_single('GtkMessageDialog'), None)
161+
162+ # focus and activate the "Greet" button
163+ btn = self.app.select_single('GtkButton', label='Greet')
164+ self.assertNotEqual(btn, None)
165+ self.mouse.click_object(btn)
166+
167+ # should get the greeting dialog
168+ self.assertThat(lambda: self.app.select_single('GtkMessageDialog', visible=True),
169+ Eventually(NotEquals(None)))
170+ md = self.app.select_single('GtkMessageDialog')
171+
172+ # we expect the message dialog to show the corresponding greeting
173+ self.assertNotEqual(md.select_single('GtkLabel',
174+ label=u'Hello Joe, you like blue.'),
175+ None)
176+
177+ # close the dialog
178+ btn = md.select_single('GtkButton', label='gtk-close')
179+ self.mouse.click_object(btn)
180+ self.assertThat(lambda: self.app.select_single('GtkMessageDialog', visible=True),
181+ Eventually(Equals(None)))
182+
183+ def test_clear(self):
184+ """Using Clear button with mouse"""
185+
186+ # type in name and color
187+ self.keyboard.type('Joe')
188+ self.keyboard.press_and_release('Tab')
189+ self.keyboard.type('blue')
190+
191+ # clear
192+ btn = self.app.select_single('GtkButton', label='gtk-delete')
193+ self.mouse.click_object(btn)
194+
195+ # entries should be clear now
196+ entries = self.app.select_many('GtkEntry')
197+ self.assertEqual(len(entries), 2)
198+ for e in entries:
199+ self.assertThat(e.text, Eventually(Equals('')))
200+
201+ def test_menu(self):
202+ """Browse the menu"""
203+
204+ file_menu = self.app.select_single('GtkMenuItem', label='_File')
205+ help_menu = self.app.select_single('GtkMenuItem', label='_Help')
206+ self.assertNotEqual(file_menu, None)
207+ self.assertNotEqual(help_menu, None)
208+
209+ # the top-level menus should be visible and thus have a rect
210+ for m in (file_menu, help_menu):
211+ self.assertGreaterEqual(m.globalRect[0], 0)
212+ self.assertGreaterEqual(m.globalRect[1], 0)
213+ self.assertGreater(m.globalRect[2], 0)
214+ self.assertGreater(m.globalRect[3], 0)
215+
216+ # the submenus are not visible by default
217+ m = self.app.select_single('GtkImageMenuItem', label='gtk-open')
218+ self.assertFalse(hasattr(m, 'globalRect'))
219+
220+ # after opening, submenus should become visible
221+ self.mouse.click_object(file_menu)
222+ # FIXME: getting a reference to this object once and then just querying
223+ # it doesn't work
224+ self.assertThat(lambda: hasattr(self.app.select_single('GtkImageMenuItem',
225+ label='gtk-open'),
226+ 'globalRect'),
227+ Eventually(Equals(True)))
228+ m = self.app.select_single('GtkImageMenuItem', label='gtk-open')
229+ self.assertGreaterEqual(m.globalRect[0], 0)
230+ self.assertGreaterEqual(m.globalRect[1], 0)
231+ self.assertGreater(m.globalRect[2], 0)
232+ self.assertGreater(m.globalRect[3], 0)
233
234=== added file 'tests/autopilot/tests/test_gnome_system_log.py'
235--- tests/autopilot/tests/test_gnome_system_log.py 1970-01-01 00:00:00 +0000
236+++ tests/autopilot/tests/test_gnome_system_log.py 2013-06-26 09:35:33 +0000
237@@ -0,0 +1,61 @@
238+# blackbox testing of autopilot API against gnome-system-log
239+# Author: Martin Pitt <martin.pitt@ubuntu.com>
240+# Copyright (C) 2013 Canonical Ltd
241+#
242+# This program is free software: you can redistribute it and/or modify
243+# it under the terms of the GNU General Public License version 3 as
244+# published by the Free Software Foundation.
245+#
246+# This program is distributed in the hope that it will be useful,
247+# but WITHOUT ANY WARRANTY; without even the implied warranty of
248+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
249+# GNU General Public License for more details.
250+#
251+# You should have received a copy of the GNU General Public License
252+# along with this program. If not, see <http://www.gnu.org/licenses/>.
253+
254+
255+from autopilot.testcase import AutopilotTestCase
256+from autopilot.matchers import Eventually
257+from testtools.matchers import Equals, NotEquals
258+
259+
260+class GnomeAppTest(AutopilotTestCase):
261+ """Test autopilot against an actual GNOME application"""
262+
263+ def setUp(self):
264+ super(GnomeAppTest, self).setUp()
265+ self.patch_environment('LANGUAGE', '')
266+ self.patch_environment('LANG', '')
267+ self.patch_environment('LC_MESSAGES', 'C')
268+ self.app = self.launch_test_application('gnome-system-log', '/etc/issue')
269+
270+ def test_filename_label(self):
271+ """Find file name label"""
272+
273+ l = self.app.select_single('GtkLabel', label=u'<b>issue</b>')
274+ self.assertNotEqual(l, None)
275+ self.assertEqual(l.visible, True)
276+
277+ def test_search(self):
278+ """Run a search"""
279+
280+ revealer = self.app.select_single('GdRevealer')
281+ self.assertNotEqual(revealer, None)
282+
283+ # search bar not visible by default
284+ self.assertEqual(revealer.child_revealed, False)
285+
286+ search_btn = self.app.select_single('GtkToggleButton')
287+ self.assertNotEqual(search_btn, None)
288+ self.mouse.click_object(search_btn)
289+
290+ # should trigger search bar
291+ self.assertThat(lambda: revealer.child_revealed, Eventually(Equals(True)))
292+ search = self.app.select_single('GtkSearchEntry', visible=True)
293+ self.assertTrue(search.has_focus)
294+
295+ # something that will not be in /etc/issue
296+ self.keyboard.type('Bogus12!')
297+ self.assertThat(lambda: self.app.select_single('GtkLabel', label=u'No matches found'),
298+ Eventually(NotEquals(None)))
299
300=== removed file 'tests/autopilot/tests/test_matching_properties.py'
301--- tests/autopilot/tests/test_matching_properties.py 2013-05-22 23:29:06 +0000
302+++ tests/autopilot/tests/test_matching_properties.py 1970-01-01 00:00:00 +0000
303@@ -1,32 +0,0 @@
304-
305-
306-from autopilot.testcase import AutopilotTestCase
307-from testtools.matchers import NotEquals
308-
309-
310-
311-class PropertyMatchingTest(AutopilotTestCase):
312-
313- def setUp(self):
314- super(PropertyMatchingTest, self).setUp()
315- self.app = self.launch_test_application('gedit')
316-
317- def test_integer_matches(self):
318- """Test property matching for integers.
319-
320- Find an opaque GtkWindow in Gedit.
321- """
322-
323- opaque_window = self.app.select_many('GtkWindow', opacity=1)
324- self.assertThat(opaque_window, NotEquals(None))
325-
326-
327- def test_string_matches(self):
328- """Match a string property.
329-
330- Find an GtkImageMenuItem named 'BookmarkOpen' in Gedit.
331- """
332-
333- bookmark_open_item = self.app.select_single('GtkImageMenuItem',
334- name='BookmarkOpen')
335- self.assertThat(bookmark_open_item, NotEquals(None))
336
337=== added file 'tests/autopilot/tests/test_properties.py'
338--- tests/autopilot/tests/test_properties.py 1970-01-01 00:00:00 +0000
339+++ tests/autopilot/tests/test_properties.py 2013-06-26 09:35:33 +0000
340@@ -0,0 +1,99 @@
341+# blackbox testing of autopilot API against our hello_color.py test GTK program
342+# Author: Martin Pitt <martin.pitt@ubuntu.com>
343+# Copyright (C) 2013 Canonical Ltd
344+#
345+# This program is free software: you can redistribute it and/or modify
346+# it under the terms of the GNU General Public License version 3 as
347+# published by the Free Software Foundation.
348+#
349+# This program is distributed in the hope that it will be useful,
350+# but WITHOUT ANY WARRANTY; without even the implied warranty of
351+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
352+# GNU General Public License for more details.
353+#
354+# You should have received a copy of the GNU General Public License
355+# along with this program. If not, see <http://www.gnu.org/licenses/>.
356+
357+import os.path
358+import unittest
359+
360+from autopilot.testcase import AutopilotTestCase
361+
362+tests_dir = os.path.dirname(os.path.dirname(os.path.dirname(
363+ os.path.realpath(__file__))))
364+test_app = os.path.join(tests_dir, 'hello_color.py')
365+
366+
367+class PropertyTest(AutopilotTestCase):
368+ """Widget properties"""
369+
370+ def setUp(self):
371+ super(PropertyTest, self).setUp()
372+ self.app = self.launch_test_application(test_app, app_type='gtk')
373+
374+ def test_button(self):
375+ """GtkButton properties"""
376+
377+ btn_greet = self.app.select_single('GtkButton', label='Greet')
378+ self.assertNotEqual(btn_greet, None)
379+ btn_quit = self.app.select_single('GtkButton', label='gtk-quit')
380+ self.assertNotEqual(btn_quit, None)
381+
382+ self.assertEqual(btn_greet.use_stock, False)
383+ self.assertEqual(btn_quit.use_stock, True)
384+
385+ # only GtkButton, GtkFileChooserButton, and GtkComboBox have
386+ # focus-on-click, and we don't use the latter two
387+ self.assertEqual(btn_greet.focus_on_click, True)
388+ self.assertEqual(btn_quit.focus_on_click, True)
389+
390+ # all buttons are visible and thus should have a rect
391+ self.assertTrue(btn_greet.visible)
392+ self.assertTrue(btn_quit.visible)
393+ self.assertEqual(len(btn_greet.globalRect), 4)
394+ self.assertEqual(len(btn_quit.globalRect), 4)
395+
396+ def test_entry(self):
397+ """GtkEntry properties"""
398+
399+ entries = self.app.select_many('GtkEntry')
400+ self.assertEqual(len(entries), 2)
401+ # the upper entry is for the name, the lower for the color
402+ # FIXME: once we have proper names (LP# 1082391), replace this with an
403+ # assertion
404+ if entries[0].globalRect[1] < entries[1].globalRect[1]:
405+ (entry_name, entry_color) = entries
406+ else:
407+ (entry_color, entry_name) = entries
408+
409+ self.assertTrue(entry_name.visible)
410+ self.assertTrue(entry_color.visible)
411+
412+ # the entries should have the same size and x alignment
413+ self.assertEqual(entry_name.globalRect[0], entry_color.globalRect[0])
414+ self.assertEqual(entry_name.globalRect[2:], entry_color.globalRect[2:])
415+
416+ # FIXME: This isn't necessary for real X, but under Xvfb there is no
417+ # default focus sometimes
418+ if not entry_name.has_focus:
419+ self.mouse.click_object(entry_name)
420+
421+ # first entry has default focus
422+ self.assertEqual(entry_name.has_focus, True)
423+ self.assertEqual(entry_color.has_focus, False)
424+
425+ # both entries are empty by default
426+ self.assertEqual(entry_name.text, '')
427+ self.assertEqual(entry_color.text, '')
428+
429+ # text-length is an unique property for GtkEntry
430+ self.assertEqual(entry_name.text_length, 0)
431+ self.assertEqual(entry_color.text_length, 0)
432+
433+ #https://launchpad.net/bugs/1193342
434+ @unittest.expectedFailure
435+ def test_enum_properties(self):
436+ '''enum properties'''
437+
438+ btn_greet = self.app.select_single('GtkButton', label='Greet')
439+ self.assertTrue(hasattr(btn_greet, 'relief'))
440
441=== added file 'tests/autopilot/tests/test_widget_tree.py'
442--- tests/autopilot/tests/test_widget_tree.py 1970-01-01 00:00:00 +0000
443+++ tests/autopilot/tests/test_widget_tree.py 2013-06-26 09:35:33 +0000
444@@ -0,0 +1,186 @@
445+# blackbox testing of autopilot API against our hello_color.py test GTK program
446+# Author: Martin Pitt <martin.pitt@ubuntu.com>
447+# Copyright (C) 2013 Canonical Ltd
448+#
449+# This program is free software: you can redistribute it and/or modify
450+# it under the terms of the GNU General Public License version 3 as
451+# published by the Free Software Foundation.
452+#
453+# This program is distributed in the hope that it will be useful,
454+# but WITHOUT ANY WARRANTY; without even the implied warranty of
455+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
456+# GNU General Public License for more details.
457+#
458+# You should have received a copy of the GNU General Public License
459+# along with this program. If not, see <http://www.gnu.org/licenses/>.
460+
461+import os.path
462+import unittest
463+
464+from autopilot.testcase import AutopilotTestCase
465+
466+tests_dir = os.path.dirname(os.path.dirname(os.path.dirname(
467+ os.path.realpath(__file__))))
468+test_app = os.path.join(tests_dir, 'hello_color.py')
469+
470+
471+class WidgetTreeTest(AutopilotTestCase):
472+ """Widget tree iteration and search"""
473+
474+ def setUp(self):
475+ super(WidgetTreeTest, self).setUp()
476+ self.app = self.launch_test_application(test_app, app_type='gtk')
477+
478+ def test_get_children_recursive(self):
479+ """Recursive get_children()
480+
481+ This should not crash, and deliver valid widgets.
482+ """
483+ widgets = set()
484+ self._get_widgets(self.app, widgets)
485+ for c in widgets:
486+ self.assertIn('.Gtk', str(type(c)))
487+ self.assertGreaterEqual(c.id, 0)
488+ # uncomment this to get a dump of all widgets and properties
489+ #print(type(c))
490+ #for p in c.get_properties():
491+ # print ' ', p, repr(getattr(c, p))
492+
493+ def test_get_children_by_type(self):
494+ # multiple instances
495+ res = self.app.get_children_by_type('GtkWindow')
496+ self.assertGreaterEqual(len(res), 3)
497+ self.assertIn('.GtkWindow', str(type(res[0])))
498+
499+ # one qualified instance
500+ res = self.app.get_children_by_type('GtkWindow', Children=['GtkBox'])
501+ self.assertGreaterEqual(len(res), 1)
502+
503+ # no instances
504+ self.assertEqual(self.app.get_children_by_type('GtkTable'), [])
505+
506+ def test_select_single_unique(self):
507+ """select_single() on widget types with only one instance"""
508+
509+ for wtype in ('GtkMenuBar', 'GtkAboutDialog', 'GtkGrid'):
510+ w = self.app.select_single(wtype)
511+ self.assertIn('.' + wtype, str(type(w)))
512+
513+ def test_select_single_nonunique(self):
514+ """select_single() on widget types with multiple instances"""
515+
516+ # we have more than one instance of these
517+ for wtype in ('GtkButton', 'GtkEntry'):
518+ self.assertRaises(ValueError, self.app.select_single, wtype)
519+
520+ # we have no instances of these
521+ for wtype in ('GtkTable', 'GtkRadioButton'):
522+ self.assertIs(self.app.select_single(wtype), None)
523+
524+ # qualified: visible property is not unique
525+ self.assertRaises(ValueError,
526+ self.app.select_single, 'GtkButton', visible=1)
527+
528+ # qualified: label property is unique within GtkButton
529+ w = self.app.select_single('GtkButton', label='gtk-quit')
530+ self.assertIn('.GtkButton', str(type(w)))
531+ self.assertEqual(w.label, 'gtk-quit')
532+
533+ def test_select_single_noclass(self):
534+ """select_single() without specifying a class"""
535+
536+ # gtk-delete label is unique to our Button
537+ w = self.app.select_single(label='gtk-delete')
538+ self.assertIn('.GtkButton', str(type(w)))
539+ self.assertEqual(w.label, 'gtk-delete')
540+
541+ # gtk-quit label is not unique globally, it's also a menu item
542+ self.assertRaises(ValueError, self.app.select_single, label='gtk-quit')
543+
544+ # ... but it is unique for focussable widgets (menus don't allow that)
545+ w = self.app.select_single(label='gtk-quit', can_focus=1)
546+ self.assertIn('.GtkButton', str(type(w)))
547+ self.assertEqual(w.label, 'gtk-quit')
548+
549+ def test_select_many_string(self):
550+ """select_many() with string properties"""
551+
552+ # by class, unqualified, multiple instances
553+ res = self.app.select_many('GtkButton')
554+ # we have three in our main window, plus some in the about dialog
555+ self.assertGreaterEqual(len(res), 3)
556+ self.assertIn('.GtkButton', str(type(res[0])))
557+
558+ # .. but exactly three in the main window
559+ main_window = self.app.select_single('GtkWindow', Children=['GtkBox'], visible=True)
560+ res = main_window.select_many('GtkButton')
561+ self.assertEqual(len(res), 3)
562+
563+ # by class, unqualified, single instance
564+ res = self.app.select_many('GtkMenuBar')
565+ self.assertEqual(len(res), 1)
566+ self.assertIn('.GtkMenuBar', str(type(res[0])))
567+
568+ # by class, unqualified, no instance
569+ res = self.app.select_many('GtkTable')
570+ self.assertEqual(res, [])
571+
572+ # by class, qualified
573+ res = self.app.select_many('GtkButton', label='Greet')
574+ self.assertEqual(len(res), 1)
575+ self.assertIn('.GtkButton', str(type(res[0])))
576+ self.assertEqual(res[0].label, 'Greet')
577+
578+ # untyped
579+ res = self.app.select_many(label='gtk-delete')
580+ self.assertEqual(len(res), 1)
581+ self.assertIn('.GtkButton', str(type(res[0])))
582+ self.assertEqual(res[0].label, 'gtk-delete')
583+
584+ res = self.app.select_many(label='gtk-quit')
585+ # button and menu item
586+ self.assertEqual(len(res), 2)
587+
588+ # https://launchpad.net/bugs/1194763
589+ @unittest.expectedFailure
590+ def test_select_int(self):
591+ """select_*() with int properties"""
592+
593+ # with class
594+ res = self.app.select_many('GtkButtonBox', border_width=5)
595+ self.assertEqual(len(res), 1)
596+
597+ self.assertNotEqual(self.app.select_single('GtkButtonBox', border_width=5), None)
598+
599+ # without class
600+ res = self.app.select_many(border_width=5)
601+ self.assertGreater(len(res), 2)
602+
603+ self.assertNotEqual(self.app.select_single(border_width=2), None)
604+
605+ # https://launchpad.net/bugs/1194763
606+ @unittest.expectedFailure
607+ def test_select_bool(self):
608+ """select_*() with boolean properties"""
609+
610+ # with class
611+ res = self.app.select_many('GtkButton', visible=True)
612+ self.assertGreater(len(res), 2)
613+
614+ res = self.app.select_many('GtkAboutDialog', visible=False)
615+ self.assertGreater(len(res), 0)
616+
617+ # without class
618+ res = self.app.select_many(visible=True)
619+ self.assertGreater(len(res), 5)
620+
621+ res = self.app.select_many(visible=False)
622+ self.assertGreater(len(res), 4)
623+
624+ @classmethod
625+ def _get_widgets(klass, obj, widget_set):
626+ """Recursively add all children of obj to widget_set"""
627+
628+ for c in obj.get_children():
629+ widget_set.add(c)
630+ klass._get_widgets(c, widget_set)
631
632=== added file 'tests/autopilot/tests/test_xpath_query.py'
633--- tests/autopilot/tests/test_xpath_query.py 1970-01-01 00:00:00 +0000
634+++ tests/autopilot/tests/test_xpath_query.py 2013-06-26 09:35:33 +0000
635@@ -0,0 +1,110 @@
636+# blackbox testing of autopilot API against our hello_color.py test GTK program
637+# Author: Martin Pitt <martin.pitt@ubuntu.com>
638+# Copyright (C) 2013 Canonical Ltd
639+#
640+# This program is free software: you can redistribute it and/or modify
641+# it under the terms of the GNU General Public License version 3 as
642+# published by the Free Software Foundation.
643+#
644+# This program is distributed in the hope that it will be useful,
645+# but WITHOUT ANY WARRANTY; without even the implied warranty of
646+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
647+# GNU General Public License for more details.
648+#
649+# You should have received a copy of the GNU General Public License
650+# along with this program. If not, see <http://www.gnu.org/licenses/>.
651+
652+import os.path
653+import unittest
654+
655+from autopilot.testcase import AutopilotTestCase
656+
657+tests_dir = os.path.dirname(os.path.dirname(os.path.dirname(
658+ os.path.realpath(__file__))))
659+test_app = os.path.join(tests_dir, 'hello_color.py')
660+
661+
662+class XPathQueryTest(AutopilotTestCase):
663+ """XPath queries"""
664+
665+ def setUp(self):
666+ super(XPathQueryTest, self).setUp()
667+ self.app = self.launch_test_application(test_app, app_type='gtk')
668+
669+ def xtest_have_path(self):
670+ """All children have a unique path"""
671+
672+ widgets = set()
673+ self._get_widgets(self.app, widgets)
674+
675+ seen_paths = set()
676+ for widget in widgets:
677+ path = widget.get_class_query_string()
678+ self.assertNotIn(path, seen_paths)
679+ seen_paths.add(path)
680+
681+ # we can resolve the path back to the widget
682+ state = self.app.get_state_by_path(path)
683+ # state is an array with one (path, props) element
684+ props = state[0][1]
685+ self.assertEqual(props['id'], widget.id)
686+ self.assertEqual(props['visible'], widget.visible)
687+
688+ def xtest_select_full_path(self):
689+ """Select widgets with full XPath"""
690+
691+ # three buttons in main dialog's ButtonBox
692+ state = self.app.get_state_by_path('/Root/GtkWindow/GtkBox/GtkButtonBox/GtkButton')
693+ self.assertEqual(len(state), 3)
694+ labels = [str(props[1]['label']) for props in state]
695+ labels.sort()
696+ self.assertEqual(labels, ['Greet', 'gtk-delete', 'gtk-quit'])
697+
698+ # select button with particular label
699+ for l in ['Greet', 'gtk-delete', 'gtk-quit']:
700+ state = self.app.get_state_by_path('/Root/GtkWindow/GtkBox/GtkButtonBox/GtkButton[label=%s]' % l)
701+ self.assertEqual(len(state), 1)
702+ self.assertEqual(state[0][1]['label'], l)
703+
704+ def xtest_select_path_pattern(self):
705+ """Select widgets with XPath path pattern"""
706+
707+ # three buttons in main dialog's ButtonBox
708+ state = self.app.get_state_by_path('//GtkWindow//GtkButton')
709+ self.assertEqual(len(state), 3)
710+ labels = [str(props[1]['label']) for props in state]
711+ labels.sort()
712+ self.assertEqual(labels, ['Greet', 'gtk-delete', 'gtk-quit'])
713+
714+ # at least four buttons in the whole tree
715+ state = self.app.get_state_by_path('/Root//GtkButton')
716+ self.assertGreaterEqual(len(state), 4)
717+
718+ def test_select_by_attribute(self):
719+ """Select widgets with attribute pattern"""
720+
721+ state = self.app.get_state_by_path('//*[label=gtk-delete]')
722+ self.assertEqual(len(state), 1, state)
723+ self.assertEqual(state[0][1]['label'], 'gtk-delete')
724+ self.assertTrue(state[0][0].endswith('/GtkButton'), state[0][0])
725+
726+ # https://launchpad.net/bugs/1179806
727+ @unittest.expectedFailure
728+ def test_select_by_attribute_spaces(self):
729+ """Select widgets with attribute pattern containing spaces"""
730+
731+ # none of these work ATM, but are supposed to:
732+ #state = self.app.get_state_by_path('//*[label=Hello&#x20;Color!]')
733+ #state = self.app.get_state_by_path('//*[label=Hello&#x0020;Color!]')
734+ state = self.app.get_state_by_path('//*[label="Hello Color!"]')
735+ self.assertEqual(len(state), 1, str(state))
736+ self.assertEqual(state[0][1]['label'], 'Hello Color!')
737+ self.assertTrue(state[0][0].endswith('/GtkLabel'), state[0][0])
738+
739+ @classmethod
740+ def _get_widgets(klass, obj, widget_set):
741+ """Recursively add all children of obj to widget_set"""
742+
743+ for c in obj.get_children():
744+ widget_set.add(c)
745+ klass._get_widgets(c, widget_set)
746
747=== added file 'tests/hello_color.py'
748--- tests/hello_color.py 1970-01-01 00:00:00 +0000
749+++ tests/hello_color.py 2013-06-26 09:35:33 +0000
750@@ -0,0 +1,53 @@
751+#!/usr/bin/python
752+
753+import sys
754+import os.path
755+
756+from gi.repository import Gtk
757+
758+
759+class HelloColorApp(Gtk.Application):
760+ def __init__(self):
761+ self.widgets = Gtk.Builder.new()
762+ self.widgets.add_from_file((os.path.join(os.path.dirname(sys.argv[0]), 'hello_color.ui')))
763+ assert self.widgets.connect_signals(self) is None
764+
765+ def run(self):
766+ self.widgets.get_object('window_app').show()
767+ Gtk.main()
768+
769+ def on_quit(self, *args):
770+ Gtk.main_quit()
771+
772+ def on_file_open(self, *args):
773+ md = Gtk.FileChooserDialog('Select a file..',
774+ parent=self.widgets.get_object('window_app'),
775+ buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
776+ Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
777+ result = md.run()
778+ md.hide()
779+ if result == Gtk.ResponseType.OK:
780+ self.widgets.get_object('label_status').set_text('Loaded %s' % md.get_filenames()[0])
781+
782+ def on_button_greet(self, *args):
783+ name = self.widgets.get_object('entry_name').get_text()
784+ color = self.widgets.get_object('entry_color').get_text()
785+
786+ md = Gtk.MessageDialog(message_type=Gtk.MessageType.INFO,
787+ buttons=Gtk.ButtonsType.CLOSE,
788+ text='Hello %s, you like %s.' % (name, color))
789+ md.run()
790+ md.hide()
791+
792+ def on_button_clear(self, *args):
793+ self.widgets.get_object('entry_name').set_text('')
794+ self.widgets.get_object('entry_color').set_text('')
795+ self.widgets.get_object('label_status').set_text('')
796+
797+ def on_about(self, *args):
798+ d = self.widgets.get_object('dialog_about')
799+ d.run()
800+ d.hide()
801+
802+if __name__ == '__main__':
803+ HelloColorApp().run()
804
805=== added file 'tests/hello_color.ui'
806--- tests/hello_color.ui 1970-01-01 00:00:00 +0000
807+++ tests/hello_color.ui 2013-06-26 09:35:33 +0000
808@@ -0,0 +1,243 @@
809+<?xml version="1.0" encoding="UTF-8"?>
810+<interface>
811+ <!-- interface-requires gtk+ 3.0 -->
812+ <object class="GtkAboutDialog" id="dialog_about">
813+ <property name="can_focus">False</property>
814+ <property name="border_width">5</property>
815+ <property name="type_hint">dialog</property>
816+ <property name="program_name">Hello Color!</property>
817+ <property name="copyright" translatable="yes">(C) 2013 Canonical Ltd.</property>
818+ <property name="authors">Martin Pitt</property>
819+ <property name="license_type">gpl-3-0</property>
820+ </object>
821+ <object class="GtkWindow" id="window_app">
822+ <property name="can_focus">False</property>
823+ <signal name="destroy" handler="on_quit" swapped="no"/>
824+ <child>
825+ <object class="GtkBox" id="box1">
826+ <property name="visible">True</property>
827+ <property name="can_focus">False</property>
828+ <property name="orientation">vertical</property>
829+ <child>
830+ <object class="GtkMenuBar" id="menubar">
831+ <property name="visible">True</property>
832+ <property name="can_focus">False</property>
833+ <child>
834+ <object class="GtkMenuItem" id="menuitem_file">
835+ <property name="visible">True</property>
836+ <property name="can_focus">False</property>
837+ <property name="label" translatable="yes">_File</property>
838+ <property name="use_underline">True</property>
839+ <child type="submenu">
840+ <object class="GtkMenu" id="menu1">
841+ <property name="visible">True</property>
842+ <property name="can_focus">False</property>
843+ <child>
844+ <object class="GtkImageMenuItem" id="menu_file_open">
845+ <property name="label">gtk-open</property>
846+ <property name="visible">True</property>
847+ <property name="can_focus">False</property>
848+ <property name="use_underline">True</property>
849+ <property name="use_stock">True</property>
850+ <signal name="activate" handler="on_file_open" swapped="no"/>
851+ </object>
852+ </child>
853+ <child>
854+ <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
855+ <property name="visible">True</property>
856+ <property name="can_focus">False</property>
857+ </object>
858+ </child>
859+ <child>
860+ <object class="GtkImageMenuItem" id="menu_item_quit">
861+ <property name="label">gtk-quit</property>
862+ <property name="visible">True</property>
863+ <property name="can_focus">False</property>
864+ <property name="use_underline">True</property>
865+ <property name="use_stock">True</property>
866+ <signal name="activate" handler="on_quit" swapped="no"/>
867+ </object>
868+ </child>
869+ </object>
870+ </child>
871+ </object>
872+ </child>
873+ <child>
874+ <object class="GtkMenuItem" id="menuitem_help">
875+ <property name="visible">True</property>
876+ <property name="can_focus">False</property>
877+ <property name="label" translatable="yes">_Help</property>
878+ <property name="use_underline">True</property>
879+ <child type="submenu">
880+ <object class="GtkMenu" id="menu2">
881+ <property name="visible">True</property>
882+ <property name="can_focus">False</property>
883+ <child>
884+ <object class="GtkImageMenuItem" id="menu_help_about">
885+ <property name="label">gtk-about</property>
886+ <property name="visible">True</property>
887+ <property name="can_focus">False</property>
888+ <property name="use_underline">True</property>
889+ <property name="use_stock">True</property>
890+ <signal name="activate" handler="on_about" swapped="no"/>
891+ </object>
892+ </child>
893+ </object>
894+ </child>
895+ </object>
896+ </child>
897+ </object>
898+ <packing>
899+ <property name="expand">False</property>
900+ <property name="fill">True</property>
901+ <property name="position">0</property>
902+ </packing>
903+ </child>
904+ <child>
905+ <object class="GtkGrid" id="grid1">
906+ <property name="visible">True</property>
907+ <property name="can_focus">False</property>
908+ <property name="halign">start</property>
909+ <property name="margin_left">5</property>
910+ <property name="margin_right">5</property>
911+ <property name="margin_top">5</property>
912+ <property name="margin_bottom">5</property>
913+ <property name="row_spacing">5</property>
914+ <property name="column_spacing">5</property>
915+ <child>
916+ <object class="GtkLabel" id="label1">
917+ <property name="visible">True</property>
918+ <property name="can_focus">False</property>
919+ <property name="label" translatable="yes">Name</property>
920+ </object>
921+ <packing>
922+ <property name="left_attach">0</property>
923+ <property name="top_attach">0</property>
924+ <property name="width">1</property>
925+ <property name="height">1</property>
926+ </packing>
927+ </child>
928+ <child>
929+ <object class="GtkLabel" id="label2">
930+ <property name="visible">True</property>
931+ <property name="can_focus">False</property>
932+ <property name="label" translatable="yes">Color</property>
933+ </object>
934+ <packing>
935+ <property name="left_attach">0</property>
936+ <property name="top_attach">1</property>
937+ <property name="width">1</property>
938+ <property name="height">1</property>
939+ </packing>
940+ </child>
941+ <child>
942+ <object class="GtkEntry" id="entry_name">
943+ <property name="visible">True</property>
944+ <property name="can_focus">True</property>
945+ <property name="has_focus">True</property>
946+ <property name="invisible_char">•</property>
947+ </object>
948+ <packing>
949+ <property name="left_attach">1</property>
950+ <property name="top_attach">0</property>
951+ <property name="width">1</property>
952+ <property name="height">1</property>
953+ </packing>
954+ </child>
955+ <child>
956+ <object class="GtkEntry" id="entry_color">
957+ <property name="visible">True</property>
958+ <property name="can_focus">True</property>
959+ <property name="invisible_char">•</property>
960+ </object>
961+ <packing>
962+ <property name="left_attach">1</property>
963+ <property name="top_attach">1</property>
964+ <property name="width">1</property>
965+ <property name="height">1</property>
966+ </packing>
967+ </child>
968+ </object>
969+ <packing>
970+ <property name="expand">True</property>
971+ <property name="fill">True</property>
972+ <property name="position">1</property>
973+ </packing>
974+ </child>
975+ <child>
976+ <object class="GtkLabel" id="label_status">
977+ <property name="visible">True</property>
978+ <property name="can_focus">False</property>
979+ <property name="xalign">0</property>
980+ <property name="xpad">5</property>
981+ <property name="ypad">10</property>
982+ </object>
983+ <packing>
984+ <property name="expand">False</property>
985+ <property name="fill">True</property>
986+ <property name="position">2</property>
987+ </packing>
988+ </child>
989+ <child>
990+ <object class="GtkButtonBox" id="buttonbox2">
991+ <property name="visible">True</property>
992+ <property name="can_focus">False</property>
993+ <property name="spacing">10</property>
994+ <property name="homogeneous">True</property>
995+ <property name="layout_style">end</property>
996+ <child>
997+ <object class="GtkButton" id="button_greet">
998+ <property name="label" translatable="yes">Greet</property>
999+ <property name="visible">True</property>
1000+ <property name="can_focus">True</property>
1001+ <property name="receives_default">True</property>
1002+ <signal name="clicked" handler="on_button_greet" swapped="no"/>
1003+ </object>
1004+ <packing>
1005+ <property name="expand">False</property>
1006+ <property name="fill">True</property>
1007+ <property name="position">0</property>
1008+ </packing>
1009+ </child>
1010+ <child>
1011+ <object class="GtkButton" id="button_clear">
1012+ <property name="label">gtk-delete</property>
1013+ <property name="visible">True</property>
1014+ <property name="can_focus">True</property>
1015+ <property name="receives_default">True</property>
1016+ <property name="use_stock">True</property>
1017+ <signal name="clicked" handler="on_button_clear" swapped="no"/>
1018+ </object>
1019+ <packing>
1020+ <property name="expand">False</property>
1021+ <property name="fill">True</property>
1022+ <property name="position">1</property>
1023+ </packing>
1024+ </child>
1025+ <child>
1026+ <object class="GtkButton" id="button_quit">
1027+ <property name="label">gtk-quit</property>
1028+ <property name="visible">True</property>
1029+ <property name="can_focus">True</property>
1030+ <property name="receives_default">True</property>
1031+ <property name="use_stock">True</property>
1032+ <property name="image_position">right</property>
1033+ <signal name="clicked" handler="on_quit" swapped="no"/>
1034+ </object>
1035+ <packing>
1036+ <property name="expand">False</property>
1037+ <property name="fill">True</property>
1038+ <property name="position">2</property>
1039+ </packing>
1040+ </child>
1041+ </object>
1042+ <packing>
1043+ <property name="expand">False</property>
1044+ <property name="fill">True</property>
1045+ <property name="position">3</property>
1046+ </packing>
1047+ </child>
1048+ </object>
1049+ </child>
1050+ </object>
1051+</interface>
1052
1053=== removed file 'tests/test-matching.sh'
1054--- tests/test-matching.sh 2013-05-22 09:11:04 +0000
1055+++ tests/test-matching.sh 1970-01-01 00:00:00 +0000
1056@@ -1,25 +0,0 @@
1057-#!/bin/bash
1058-#
1059-
1060-# gedit --gtk-module `pwd`/lib/libautopilot.so &
1061-# GEDIT_PID=$!
1062-# sleep 2
1063-
1064-EMPTY_SET='[Argument: a(sv) {}]'
1065-RES=1
1066-
1067-RET=`qdbus --literal org.gnome.gedit /com/canonical/Autopilot/Introspection com.canonical.Autopilot.Introspection.GetState '/Root//GtkWindow[opacity=1]'`
1068-if [ "$RET" == "$EMPTY_SET" ]; then
1069- echo "FAIL"
1070- RES=0
1071-fi
1072-
1073-RET=`qdbus --literal org.gnome.gedit /com/canonical/Autopilot/Introspection com.canonical.Autopilot.Introspection.GetState '/Root//GtkWindow/GtkMenu/GtkImageMenuItem[name=BookmarkOpen]'`
1074-if [ "$RET" == "$EMPTY_SET" ]; then
1075- echo "FAIL"
1076- RES=0
1077-fi
1078-
1079-
1080-# kill $GEDIT_PID
1081-exit $RES

Subscribers

People subscribed via source and target branches

to all changes: