Merge lp:~canonical-platform-qa/ubuntu-system-tests/app-launch-introspect into lp:ubuntu-system-tests

Proposed by Richard Huddie
Status: Merged
Approved by: Santiago Baldassin
Approved revision: 539
Merged at revision: 514
Proposed branch: lp:~canonical-platform-qa/ubuntu-system-tests/app-launch-introspect
Merge into: lp:ubuntu-system-tests
Diff against target: 1503 lines (+396/-572)
25 files modified
ubuntu_system_tests/common/config.py (+2/-3)
ubuntu_system_tests/helpers/addressbook/app.py (+3/-4)
ubuntu_system_tests/helpers/application.py (+115/-142)
ubuntu_system_tests/helpers/calculator/app.py (+5/-6)
ubuntu_system_tests/helpers/calculator/config.py (+3/-5)
ubuntu_system_tests/helpers/calendar/app.py (+5/-6)
ubuntu_system_tests/helpers/camera/app.py (+5/-6)
ubuntu_system_tests/helpers/clock/app.py (+5/-6)
ubuntu_system_tests/helpers/dialer_app/app.py (+2/-2)
ubuntu_system_tests/helpers/filemanager/app.py (+5/-6)
ubuntu_system_tests/helpers/gallery/app.py (+7/-7)
ubuntu_system_tests/helpers/gallery/cpo.py (+14/-0)
ubuntu_system_tests/helpers/messaging/app.py (+2/-2)
ubuntu_system_tests/helpers/snap.py (+21/-7)
ubuntu_system_tests/helpers/system_settings/app.py (+42/-14)
ubuntu_system_tests/helpers/telegram/app.py (+2/-2)
ubuntu_system_tests/helpers/terminal/app.py (+9/-8)
ubuntu_system_tests/helpers/terminal/cpo.py (+46/-0)
ubuntu_system_tests/helpers/utils.py (+8/-8)
ubuntu_system_tests/helpers/webbrowser/app.py (+5/-6)
ubuntu_system_tests/host/target_setup.py (+32/-5)
ubuntu_system_tests/host/targets.py (+1/-1)
ubuntu_system_tests/tests/base.py (+4/-4)
ubuntu_system_tests/tests/test_launch_apps.py (+45/-320)
ubuntu_system_tests/tests/test_system_settings.py (+8/-2)
To merge this branch: bzr merge lp:~canonical-platform-qa/ubuntu-system-tests/app-launch-introspect
Reviewer Review Type Date Requested Status
Santiago Baldassin (community) Approve
platform-qa-bot continuous-integration Approve
Review via email: mp+318509@code.launchpad.net

Commit message

Use introspection in app launch tests to validate app has launched.

Description of the change

Changes:
- Change config value package_type to unity8_mode for clarity
- Add Snap and Deb application classes, derived from Application.
- Add some missing cpos used to validate app has launched
- Fix the is_discovery() method which was not working correctly when checking if an app is installed.
- Update target_setup.py script to install snap version of apps on a deb session.
- Remove all image analysis from test_launch_apps and replace with introspection.

Currently only the deb version works when launching app once on unity8 deb session. In this case there should be 2 pass and 8 failed. All other variants will fail due to problems with app launch tools.

To test:

python3 -m ubuntu_system_tests.run run ubuntu_system_tests.tests.test_launch_apps.LaunchAppOnceTestCase

python3 -m ubuntu_system_tests.run run ubuntu_system_tests.tests.test_launch_apps.LaunchAppTwiceTestCase

This should be landed in conjunction with following for qa-jenkins-jobs: https://code.launchpad.net/~canonical-platform-qa/qa-jenkins-jobs/ust-add-app-modes/+merge/319313

To post a comment you must log in.
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Santiago Baldassin (sbaldassin) wrote :

Thanks for the mp Richard. I'm afraid I disagree on the design followed. With minimal changes we would be able to run tests against an image which has both snaps and deb installed, all as part of the same job. Would you please take a look at this? https://pastebin.ubuntu.com/24140328/

I've just run ./system-tests run ubuntu_system_tests.tests.test_launch_apps.LaunchAppOnceTestCase with the design above and I was able to run the tests for calc, webbrowser and system settings as part of the same execution

review: Needs Fixing
Revision history for this message
Richard Huddie (rhuddie) wrote :

The reason for doing this way is to write the tests without knowledge of the type of app being used. - Hence using the config value.

Although the app-launch tests could be implemented with snap/deb specific knowledge, it wouldn't be good to have a suite of sanity or regression tests where we need to have specific test cases for specific app versions. - The way I see it is you would want to run a sanity suite on a deb image for deb apps and then run exactly the same set of tests on same image for snap apps. With the current way that would just mean changing the config value and re-running the same set of tests on same image. Similarly for a snap based image, you would want to run the suite using snap apps only.

The jenkins jobs I am creating will do this from a single job. See: https://code.launchpad.net/~canonical-platform-qa/qa-jenkins-jobs/ust-add-app-modes/+merge/319313

I'm not sure if I understood your concerns correctly or not, what do you think?

Revision history for this message
Santiago Baldassin (sbaldassin) wrote :

> The reason for doing this way is to write the tests without knowledge of the
> type of app being used. - Hence using the config value.
>
> Although the app-launch tests could be implemented with snap/deb specific
> knowledge, it wouldn't be good to have a suite of sanity or regression tests
> where we need to have specific test cases for specific app versions.

This is actually my point, with the approach I suggested, the tests do not know if they are dealing with a snap or a deb, they are just dealing with an application. So for example in the case of the app launch test cases, the test would call calculator.launch() or web_browser.launch(), and since they are both extending from different classes, the apps will be launched differently.

Right now, we specify a mode and we skip those tests that do not comply with that mode right? With the approach I suggest, we don't specify a mode at all. Every app (either deb or snap) knows how to launch itself

 - The way
> I see it is you would want to run a sanity suite on a deb image for deb apps
> and then run exactly the same set of tests on same image for snap apps.

There you go...so this is what we need to agree on. The way I see it, you have an image and you want to run the sanity suite against that image, no matter what that image has, either debs or snaps or both, as you said, the test do not care

If we follow the mode approach, we would have to start skipping test cases based on the mode right? just as tests are skipped here. And when a deb is moved to snap we'll need to do the other way around. With the approach that I proposed, we don't skip tests at all and when a deb app is snapped, then we just change the inheritance from Deb to Snap

With
> the current way that would just mean changing the config value and re-running
> the same set of tests on same image. Similarly for a snap based image, you
> would want to run the suite using snap apps only.
>

From my point of view the suite of tests is exactly the same either in a pure snap, pure deb or in a mix of debs and snaps like we have now. The tests do not change, what changes, is how you start ans introspect a app or a snap

> The jenkins jobs I am creating will do this from a single job. See:
> https://code.launchpad.net/~canonical-platform-qa/qa-jenkins-jobs/ust-add-app-
> modes/+merge/319313
>

I was not clear. What I don't want to do is to re run the tests and merge the results when we could run the tests once and avoid the post processing

> I'm not sure if I understood your concerns correctly or not, what do you
> think?

Let's go through this over the phone. It'll be much easier, and we can get everyone opinion on this

Revision history for this message
Richard Huddie (rhuddie) wrote :

> This is actually my point, with the approach I suggested, the tests do not
> know if they are dealing with a snap or a deb, they are just dealing with an
> application. So for example in the case of the app launch test cases, the test
> would call calculator.launch() or web_browser.launch(), and since they are
> both extending from different classes, the apps will be launched differently.

But this would only work for for one instance of the app, either deb or snap, right? So if you wanted to run your tests against both deb and snap versions of a single application (as is the case with the app launch tests) you would have to change the base class to do so.

If we were running these tests on an deb based image we would want to test both deb and snap versions of the app, but for a snap based image we would only want to test the snap version of the app. I don't see how the approach you mentioned could work with that scenario?

>
> If we follow the mode approach, we would have to start skipping test cases
> based on the mode right? just as tests are skipped here. And when a deb is
> moved to snap we'll need to do the other way around. With the approach that I
> proposed, we don't skip tests at all and when a deb app is snapped, then we
> just change the inheritance from Deb to Snap

I think this is the key point. Doing this would require a code change, and does not allow for testing both deb and snap versions of a single application at the same time.

>
> Let's go through this over the phone. It'll be much easier, and we can get
> everyone opinion on this

Sure, we can discuss more later.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
539. By Richard Huddie

Fix flake8.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Santiago Baldassin (sbaldassin) wrote :

Code looks good to me

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntu_system_tests/common/config.py'
2--- ubuntu_system_tests/common/config.py 2017-03-06 17:27:25 +0000
3+++ ubuntu_system_tests/common/config.py 2017-03-14 11:03:55 +0000
4@@ -211,10 +211,9 @@
5 default='qemu',
6 help_string='Type of target. Either qemu or adb.'),
7 options.Option(
8- 'package_type',
9+ 'unity8_mode',
10 default='deb',
11- help_string='Which unity8 package type should be installed during '
12- 'setup. Either snap or deb.'),
13+ help_string='Which unity8 session to use, either deb or snap.'),
14 ]
15
16
17
18=== modified file 'ubuntu_system_tests/helpers/addressbook/app.py'
19--- ubuntu_system_tests/helpers/addressbook/app.py 2017-02-08 15:03:35 +0000
20+++ ubuntu_system_tests/helpers/addressbook/app.py 2017-03-14 11:03:55 +0000
21@@ -18,21 +18,20 @@
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #
24 from collections import namedtuple
25-from ubuntu_system_tests.helpers.application import Application
26+from ubuntu_system_tests.helpers.application import Snap
27
28 APP_NAME = 'Contacts'
29 APP_ID = 'address-book-app'
30-APP_SNAP = 'address-book-app'
31
32
33 Contact = namedtuple('Contact', ['name', 'number', 'expected_reply'])
34 Contact.__new__.__defaults__ = (None, None, None)
35
36
37-class AddressBook(Application):
38+class AddressBook(Snap):
39
40 def __init__(self):
41- super().__init__(APP_NAME, APP_ID, APP_SNAP)
42+ super().__init__(APP_NAME, APP_ID)
43
44 def get_main_view(self):
45 # TODO Move the address book helper upstream migrating the
46
47=== modified file 'ubuntu_system_tests/helpers/application.py'
48--- ubuntu_system_tests/helpers/application.py 2017-03-08 01:00:05 +0000
49+++ ubuntu_system_tests/helpers/application.py 2017-03-14 11:03:55 +0000
50@@ -29,76 +29,75 @@
51
52 from ubuntu_system_tests.helpers import autopilot
53 from ubuntu_system_tests.helpers import processes
54-from ubuntu_system_tests.helpers.scopes.apps import (
55- launch_application_from_apps_scope)
56+from ubuntu_system_tests.helpers.snap import (
57+ get_snap_revision,
58+ is_installed as is_snap_installed,
59+)
60 from ubuntu_system_tests.helpers.unity8.utils import (
61 is_unity8_snap,
62 launch_application_from_launcher,
63 )
64 from ubuntu_system_tests.helpers.utils import is_discovery
65
66-UNITY8_DEB_CMD_PREFIX = ''
67-UNITY8_SNAP_CMD_PREFIX = 'unity8-session.'
68-APP_SNAP_URL = 'appid://{n}/{n}/current-user-version'
69-APP_DEB_URL = 'application:///{n}.desktop'
70-
71 logger = logging.getLogger(__name__)
72-
73-
74-class ApplicationManagerBase:
75- """Base class to launch and close unity8 apps."""
76-
77- def __init__(self, name, cmd_prefix, launch_param):
78- self.name = name
79- self.cmd_prefix = cmd_prefix
80- self.launch_param = launch_param
81- self.rev_id = self._get_rev_id()
82-
83- def _get_app_ids(self):
84- """Return list of installed app ids."""
85- return subprocess.check_output(
86- self._get_ids_command()).decode().splitlines()
87-
88- def _get_rev_id(self):
89+logger.setLevel(logging.INFO)
90+
91+
92+class Application:
93+ __metaclass__ = ABCMeta
94+
95+ def __init__(self, app_name, app_id):
96+ """
97+ :param app_name: Display name for application.
98+ :param app_id: Application id.
99+ """
100+ self.app_name = app_name
101+ self.app_id = app_id
102+ self.proxy_object = None
103+ self.cmd_prefix = None
104+
105+ def _get_cmd_prefix(self):
106+ """Return prefix to use with all ubuntu-app-launch commands.
107+ This will depend on whether unity8 is running from a snap
108+ session or not.
109+ """
110+ if self.cmd_prefix is None:
111+ self.cmd_prefix = 'unity8-session.' if is_unity8_snap() else ''
112+ return self.cmd_prefix
113+
114+ @abstractmethod
115+ def _get_app_rev_id(self, app_id):
116 """Return the application revision ID."""
117- for app_id in self._get_app_ids():
118- if app_id.startswith(self.name):
119- return app_id
120- logger.warning(
121- 'No id found for app: {}. '
122- 'Proceeding with this id value.'.format(self.name))
123- return self.name
124+ pass
125
126 def _get_start_command(self):
127 """Return command to start application."""
128 return [
129- self.cmd_prefix + 'url-dispatcher',
130- self.launch_param.format(n=self.name)]
131+ self._get_cmd_prefix() + 'url-dispatcher',
132+ self._get_launch_params()]
133
134 def _get_stop_command(self):
135 """Return command to stop application."""
136- return [self.cmd_prefix + 'ubuntu-app-stop', self.rev_id]
137+ id = self._get_app_rev_id()
138+ return [self._get_cmd_prefix() + 'ubuntu-app-stop', id]
139
140 def _get_pid_command(self):
141 """Return command to get application process ID."""
142- return [self.cmd_prefix + 'ubuntu-app-pid', self.rev_id]
143-
144- def _get_ids_command(self):
145- """Return command to get list of available application IDs."""
146- return [self.cmd_prefix + 'ubuntu-app-launch-appids']
147-
148- def launch(self):
149- """Launch application."""
150- subprocess.check_call(self._get_start_command())
151+ id = self._get_app_rev_id()
152+ return [self._get_cmd_prefix() + 'ubuntu-app-pid', id]
153
154 def stop(self):
155 """Stop application."""
156- subprocess.check_call(self._get_stop_command())
157+ cmd = self._get_stop_command()
158+ logger.info('application stop command: {}'.format(' '.join(cmd)))
159+ subprocess.check_call(cmd)
160
161- def pid(self):
162+ def get_pid(self):
163 """Return application process ID."""
164+ cmd = self._get_pid_command()
165+ logger.info('application pid command: {}'.format(' '.join(cmd)))
166 try:
167- out = subprocess.check_output(self._get_pid_command()).decode()
168+ out = subprocess.check_output(cmd).decode()
169 except subprocess.CalledProcessError:
170 pass
171 else:
172@@ -106,84 +105,24 @@
173 if line.isdigit():
174 return line
175 raise RuntimeError(
176- 'Process ID not found for app ID: {}'.format(self.rev_id))
177+ 'Process ID not found for app ID: {}'.format(self.app_id))
178
179 def running(self):
180 """Return boolean to indicate if application is running."""
181 return subprocess.call(self._get_pid_command()) == 0
182
183- def installed(self):
184- """Return boolean to indicate if application is installed."""
185- app_ids = self._get_app_ids()
186- return any(app_id.startswith(self.rev_id) for app_id in app_ids)
187-
188-
189-class DebApplicationManager(ApplicationManagerBase):
190- """Application manager for deb based apps on deb based unity8 session."""
191-
192- def __init__(self, name):
193- super().__init__(name, UNITY8_DEB_CMD_PREFIX, APP_DEB_URL)
194-
195-
196-class SnapApplicationManager(ApplicationManagerBase):
197- """Application manager for snap based apps on snap based unity8 session."""
198-
199- def __init__(self, name):
200- super().__init__(name, UNITY8_SNAP_CMD_PREFIX, APP_SNAP_URL)
201-
202-
203-class SnapOnDebApplicationManager(ApplicationManagerBase):
204- """Application manager for snap based apps on deb based unity8 session."""
205-
206- def __init__(self, name):
207- super().__init__(name, UNITY8_DEB_CMD_PREFIX, APP_SNAP_URL)
208-
209-
210-class Application:
211- __metaclass__ = ABCMeta
212-
213- def __init__(self, app_name, app_id=None, app_snap=None, use_snap=False):
214- """
215- :param app_name: Display name for application.
216- :param app_id: Application id.
217- :param app_snap: Name of application snap.
218- :param use_snap: Use snap version of application even if running from
219- a deb based unity8 session.
220- """
221- self.app_name = app_name
222- self.app_id = app_id
223- self.app_snap = app_snap
224- self.proxy_object = None
225- self.app_manager = None
226- self.use_snap = use_snap
227-
228- def _get_application_manager(self):
229- """Get application manager for required application. The type of
230- application used will either be deb or snap based depending on which
231- type of unity8 session is being used. In the case where unity8 is deb
232- based but use_snap property is True, the snap version of app is used.
233- """
234- if self.app_manager is None:
235- if is_unity8_snap():
236- # This is a snap based unity8 session, so must use snap
237- # based application.
238- self.app_manager = SnapApplicationManager(self.app_snap)
239- elif self.use_snap:
240- # This is a deb based unity8 session, but we're requested to
241- # use a snap based application.
242- self.app_manager = SnapOnDebApplicationManager(self.app_snap)
243- else:
244- # This is a deb based unity8 session using deb based
245- # application.
246- self.app_manager = DebApplicationManager(self.app_id)
247-
248- def launch_from_scope(self, return_proxy=True):
249- """
250- Launch app from apps scope
251- """
252- launch_application_from_apps_scope(self.app_name)
253- if return_proxy:
254- return self.get_proxy_object()
255+ def launch(self):
256+ """Launch application using command."""
257+ cmd = self._get_start_command()
258+ logger.info('application launch command: {}'.format(' '.join(cmd)))
259+ subprocess.check_call(cmd)
260+
261+ def launch_from_app_drawer(self, return_proxy=True):
262+ """
263+ Launch app from app drawer.
264+ """
265+ # TODO: Implement with app drawer helpers.
266+ pass
267
268 def launch_from_launcher(self, return_proxy=True):
269 """Drag out the launcher and tap the application icon to start it."""
270@@ -191,33 +130,16 @@
271 if return_proxy:
272 return self.get_proxy_object()
273
274- def launch(self):
275- self._get_application_manager()
276- self.app_manager.launch()
277-
278- def stop(self):
279- self._get_application_manager()
280- self.app_manager.stop()
281-
282- def get_pid(self):
283- self._get_application_manager()
284- return self.app_manager.pid()
285-
286- def is_running(self):
287- self._get_application_manager()
288- return self.app_manager.running()
289-
290- @retry(stop_max_delay=30000,
291- wait_exponential_multiplier=1000,
292- wait_exponential_max=10000,
293+ @retry(stop_max_delay=3000,
294+ wait_fixed=1000,
295 retry_on_exception=lambda exception: (
296 isinstance(exception, ProcessSearchError) or
297 isinstance(exception, processes.PidNotFoundError) or
298+ isinstance(exception, RuntimeError) or
299 isinstance(exception, NoSuchProcess)))
300 def get_proxy_object(self):
301 if self.proxy_object is None:
302- self.proxy_object = autopilot.get_proxy_object(
303- int(self.get_pid()))
304+ self.proxy_object = autopilot.get_proxy_object(int(self.get_pid()))
305 return self.proxy_object
306
307 @abstractmethod
308@@ -231,5 +153,56 @@
309 # In this case always return True to ensure all tests can be
310 # listed on the host machine.
311 return True
312- self._get_application_manager()
313- return self.app_manager.installed()
314+ return self._is_installed()
315+
316+ @abstractmethod
317+ def _is_installed(self, app_id):
318+ """Return boolean indicating if app is installed."""
319+ pass
320+
321+
322+class Snap(Application):
323+
324+ app_type = 'snap'
325+
326+ def __init__(self, app_name, app_id):
327+ super().__init__(app_name, app_id)
328+ self.app_rev_id = None
329+
330+ def _get_launch_params(self):
331+ """Return required parameters for app launch command."""
332+ return 'appid://{n}/{n}/current-user-version'.format(n=self.app_id)
333+
334+ def _get_app_rev_id(self):
335+ """Return the application revision ID."""
336+ if self.app_rev_id is None:
337+ rev = get_snap_revision(self.app_id)
338+ self.app_rev_id = '{n}_{n}_{r}'.format(n=self.app_id, r=rev)
339+ return self.app_rev_id
340+
341+ def _is_installed(self):
342+ """Return boolean indicating if app is installed."""
343+ return is_snap_installed(self.app_id)
344+
345+
346+class Deb(Application):
347+
348+ app_type = 'deb'
349+
350+ def __init__(self, app_name, app_id, package_id=None):
351+ super().__init__(app_name, app_id)
352+ self.package_id = package_id or app_id
353+
354+ def _get_launch_params(self):
355+ """Return required parameters for app launch command."""
356+ return 'application:///{n}.desktop'.format(n=self.app_id)
357+
358+ def _get_app_rev_id(self):
359+ """Return the application revision ID."""
360+ return self.app_id
361+
362+ def _is_installed(self):
363+ """Return boolean indicating if app is installed."""
364+ output = subprocess.check_output(
365+ ['apt-cache', 'policy', self.package_id])
366+ return len(output) > 0 and 'Installed: (none)' not in output.decode()
367
368=== modified file 'ubuntu_system_tests/helpers/calculator/app.py'
369--- ubuntu_system_tests/helpers/calculator/app.py 2017-02-09 15:14:56 +0000
370+++ ubuntu_system_tests/helpers/calculator/app.py 2017-03-14 11:03:55 +0000
371@@ -18,17 +18,16 @@
372 # along with this program. If not, see <http://www.gnu.org/licenses/>.
373 #
374
375-from ubuntu_system_tests.helpers.application import Application
376+from ubuntu_system_tests.helpers.application import Snap
377
378 APP_NAME = 'Calculator'
379 APP_ID = 'ubuntu-calculator-app'
380-APP_SNAP = 'ubuntu-calculator-app'
381-
382-
383-class Calculator(Application):
384+
385+
386+class Calculator(Snap):
387
388 def __init__(self):
389- super().__init__(APP_NAME, APP_ID, APP_SNAP)
390+ super().__init__(APP_NAME, APP_ID)
391
392 def get_main_view(self):
393 from ubuntu_system_tests.helpers.calculator import cpo # NOQA
394
395=== modified file 'ubuntu_system_tests/helpers/calculator/config.py'
396--- ubuntu_system_tests/helpers/calculator/config.py 2017-01-26 13:44:38 +0000
397+++ ubuntu_system_tests/helpers/calculator/config.py 2017-03-14 11:03:55 +0000
398@@ -19,9 +19,7 @@
399 #
400 import os
401 from ubuntu_system_tests.helpers import file_system as fs
402-from ubuntu_system_tests.helpers.calculator.app import (
403- APP_SNAP as SNAP_CALCULATOR,
404-)
405+from ubuntu_system_tests.helpers.calculator.app import APP_ID
406 from ubuntu_system_tests.helpers.configuration import Config
407 from ubuntu_system_tests.helpers.snap import (
408 get_snap_revision,
409@@ -35,9 +33,9 @@
410
411 def __init__(self):
412 if is_unity8_snap():
413- rev = get_snap_revision(SNAP_CALCULATOR)
414+ rev = get_snap_revision(APP_ID)
415 conf_dir = fs.get_home_snap_path(
416- SNAP_CALCULATOR, rev, fs.DIR_CONFIG, fs.DIR_CALCULATOR)
417+ APP_ID, rev, fs.DIR_CONFIG, fs.DIR_CALCULATOR)
418 else:
419 conf_dir = os.path.join(fs.DIR_HOME_CONFIG, fs.DIR_CALCULATOR)
420 self.conf_file_path = os.path.join(conf_dir, CALCULATOR_CONF_FILE)
421
422=== modified file 'ubuntu_system_tests/helpers/calendar/app.py'
423--- ubuntu_system_tests/helpers/calendar/app.py 2017-02-08 17:47:07 +0000
424+++ ubuntu_system_tests/helpers/calendar/app.py 2017-03-14 11:03:55 +0000
425@@ -18,17 +18,16 @@
426 # along with this program. If not, see <http://www.gnu.org/licenses/>.
427 #
428
429-from ubuntu_system_tests.helpers.application import Application
430+from ubuntu_system_tests.helpers.application import Snap
431
432 APP_NAME = 'Calendar'
433 APP_ID = 'ubuntu-calendar-app'
434-APP_SNAP = 'ubuntu-calendar-app'
435-
436-
437-class Calendar(Application):
438+
439+
440+class Calendar(Snap):
441
442 def __init__(self):
443- super().__init__(APP_NAME, APP_ID, APP_SNAP)
444+ super().__init__(APP_NAME, APP_ID)
445
446 def get_main_view(self):
447 from ubuntu_system_tests.helpers.clock.cpo import MainView # NOQA
448
449=== modified file 'ubuntu_system_tests/helpers/camera/app.py'
450--- ubuntu_system_tests/helpers/camera/app.py 2017-02-08 17:47:07 +0000
451+++ ubuntu_system_tests/helpers/camera/app.py 2017-03-14 11:03:55 +0000
452@@ -17,17 +17,16 @@
453 # along with this program. If not, see <http://www.gnu.org/licenses/>.
454 #
455
456-from ubuntu_system_tests.helpers.application import Application
457+from ubuntu_system_tests.helpers.application import Snap
458
459 APP_NAME = 'Camera'
460 APP_ID = 'camera-app'
461-APP_SNAP = 'camera-app'
462-
463-
464-class Camera(Application):
465+
466+
467+class Camera(Snap):
468
469 def __init__(self):
470- super().__init__(APP_NAME, APP_ID, APP_SNAP)
471+ super().__init__(APP_NAME, APP_ID)
472
473 def get_main_view(self):
474 from ubuntu_system_tests.helpers.camera import cpo # NOQA
475
476=== modified file 'ubuntu_system_tests/helpers/clock/app.py'
477--- ubuntu_system_tests/helpers/clock/app.py 2017-02-08 17:47:07 +0000
478+++ ubuntu_system_tests/helpers/clock/app.py 2017-03-14 11:03:55 +0000
479@@ -17,17 +17,16 @@
480 # along with this program. If not, see <http://www.gnu.org/licenses/>.
481 #
482
483-from ubuntu_system_tests.helpers.application import Application
484+from ubuntu_system_tests.helpers.application import Snap
485
486 APP_NAME = 'Clock'
487 APP_ID = 'ubuntu-clock-app'
488-APP_SNAP = 'ubuntu-clock-app'
489-
490-
491-class Clock(Application):
492+
493+
494+class Clock(Snap):
495
496 def __init__(self):
497- super().__init__(APP_NAME, APP_ID, APP_SNAP)
498+ super().__init__(APP_NAME, APP_ID)
499
500 def get_main_view(self):
501 from ubuntu_system_tests.helpers.clock.cpo import MainView # NOQA
502
503=== modified file 'ubuntu_system_tests/helpers/dialer_app/app.py'
504--- ubuntu_system_tests/helpers/dialer_app/app.py 2017-02-08 15:03:35 +0000
505+++ ubuntu_system_tests/helpers/dialer_app/app.py 2017-03-14 11:03:55 +0000
506@@ -17,13 +17,13 @@
507 # You should have received a copy of the GNU General Public License
508 # along with this program. If not, see <http://www.gnu.org/licenses/>.
509 #
510-from ubuntu_system_tests.helpers.application import Application
511+from ubuntu_system_tests.helpers.application import Snap
512
513 APP_NAME = 'Phone'
514 APP_ID = 'com.ubuntu.dialer'
515
516
517-class Dialer(Application):
518+class Dialer(Snap):
519
520 def __init__(self):
521 super().__init__(APP_NAME, APP_ID)
522
523=== modified file 'ubuntu_system_tests/helpers/filemanager/app.py'
524--- ubuntu_system_tests/helpers/filemanager/app.py 2017-02-09 11:43:45 +0000
525+++ ubuntu_system_tests/helpers/filemanager/app.py 2017-03-14 11:03:55 +0000
526@@ -18,17 +18,16 @@
527 # along with this program. If not, see <http://www.gnu.org/licenses/>.
528 #
529
530-from ubuntu_system_tests.helpers.application import Application
531+from ubuntu_system_tests.helpers.application import Snap
532
533 APP_NAME = 'Files'
534 APP_ID = 'ubuntu-filemanager-app'
535-APP_SNAP = 'ubuntu-filemanager-app'
536-
537-
538-class FileManager(Application):
539+
540+
541+class FileManager(Snap):
542
543 def __init__(self):
544- super().__init__(APP_NAME, APP_ID, APP_SNAP)
545+ super().__init__(APP_NAME, APP_ID)
546
547 def get_main_view(self):
548 pass
549
550=== modified file 'ubuntu_system_tests/helpers/gallery/app.py'
551--- ubuntu_system_tests/helpers/gallery/app.py 2017-02-08 17:47:07 +0000
552+++ ubuntu_system_tests/helpers/gallery/app.py 2017-03-14 11:03:55 +0000
553@@ -20,7 +20,7 @@
554
555 from ubuntu_system_tests.helpers import autopilot
556 from ubuntu_system_tests.helpers import processes
557-from ubuntu_system_tests.helpers.application import Application
558+from ubuntu_system_tests.helpers.application import Snap
559 from ubuntu_system_tests.helpers.scopes.apps import (
560 launch_application_from_apps_scope
561 )
562@@ -33,19 +33,19 @@
563 APP_NAME = 'Gallery'
564 APP_WIN_ID = 'com.ubuntu.gallery_gallery'
565 APP_CLICK = 'com.ubuntu.gallery'
566+APP_DOMAIN = 'com.ubuntu.gallery'
567 APP_ICON_NAME = 'gallery-app.png'
568 APP_ID = 'gallery-app'
569-APP_SNAP = 'gallery-app'
570-
571-
572-class Gallery(Application):
573+
574+
575+class Gallery(Snap):
576
577 def __init__(self):
578- super().__init__(APP_NAME, APP_ID, APP_SNAP)
579+ super().__init__(APP_NAME, APP_ID)
580
581 def get_main_view(self):
582 from ubuntu_system_tests.helpers.gallery import cpo # NOQA
583- return self.get_proxy_object().events_view
584+ return self.get_proxy_object().application
585
586
587 def launch_gallery_app():
588
589=== modified file 'ubuntu_system_tests/helpers/gallery/cpo.py'
590--- ubuntu_system_tests/helpers/gallery/cpo.py 2016-12-19 18:50:17 +0000
591+++ ubuntu_system_tests/helpers/gallery/cpo.py 2017-03-14 11:03:55 +0000
592@@ -22,6 +22,7 @@
593 from autopilot.introspection.utilities import sort_by_keys
594 from retrying import retry
595
596+from ubuntu_system_tests.helpers.gallery.app import APP_DOMAIN
597 from ubuntu_system_tests.helpers.input_manager import input_manager
598 from ubuntu_system_tests.helpers.ubuntuuitools.cpo.header import Header
599 from ubuntu_system_tests.helpers.ubuntuuitools.cpo.common import CustomProxyObjectBase # NOQA
600@@ -34,6 +35,19 @@
601 APP_PATH_ROOT = b'comubuntugallery'
602
603
604+class comubuntugallery(CustomProxyObjectBase):
605+
606+ @classmethod
607+ def validate_dbus_object(cls, path, state):
608+ return validate_dbus_object(
609+ path, state, APP_PATH_ROOT, APP_PATH_ROOT,
610+ applicationName=APP_DOMAIN)
611+
612+ @property
613+ def application(self):
614+ return self.select_single(GalleryApplication, visible=True)
615+
616+
617 class GalleryApplication(CustomProxyObjectBase):
618
619 @classmethod
620
621=== modified file 'ubuntu_system_tests/helpers/messaging/app.py'
622--- ubuntu_system_tests/helpers/messaging/app.py 2017-02-08 15:03:35 +0000
623+++ ubuntu_system_tests/helpers/messaging/app.py 2017-03-14 11:03:55 +0000
624@@ -17,7 +17,7 @@
625 # along with this program. If not, see <http://www.gnu.org/licenses/>.
626 #
627
628-from ubuntu_system_tests.helpers.application import Application
629+from ubuntu_system_tests.helpers.application import Snap
630 from ubuntu_system_tests.helpers import autopilot
631 from ubuntu_system_tests.helpers import processes
632 from ubuntu_system_tests.helpers.scopes.apps import (
633@@ -34,7 +34,7 @@
634 APP_WIN_ID = 'messaging-app'
635
636
637-class Messaging(Application):
638+class Messaging(Snap):
639
640 def __init__(self):
641 super().__init__(APP_NAME, APP_ID)
642
643=== modified file 'ubuntu_system_tests/helpers/snap.py'
644--- ubuntu_system_tests/helpers/snap.py 2017-01-26 14:06:10 +0000
645+++ ubuntu_system_tests/helpers/snap.py 2017-03-14 11:03:55 +0000
646@@ -25,15 +25,29 @@
647 SOCK_SNAPD = '/run/snapd.socket'
648
649
650+def get_snap_data(snap_name):
651+ """Return snap data dictionary or None if not found."""
652+ session = requests_unixsocket.Session()
653+ path = quote_plus(SOCK_SNAPD)
654+ url = 'http+unix://{p}/v2/snaps/{s}'.format(p=path, s=snap_name)
655+ response = session.get(url)
656+ if not response.ok:
657+ return None
658+ return json.loads(response.content.decode())['result']
659+
660+
661 def get_snap_revision(snap_name):
662 """Return the revision of the specified snap.
663 :param snap_name: Name of the required snap package.
664 :return: Revision number as string.
665 """
666- session = requests_unixsocket.Session()
667- path = quote_plus(SOCK_SNAPD)
668- url = 'http+unix://{p}/v2/snaps/{s}'.format(p=path, s=snap_name)
669- response = session.get(url)
670- if not response.ok:
671- raise ConnectionError('Could not query snapd socket: {}'.format(url))
672- return json.loads(response.content.decode())['result']['revision']
673+ snap_data = get_snap_data(snap_name)
674+ if snap_data is None:
675+ raise RuntimeError(
676+ 'Snap {s} not found'.format(s=snap_name))
677+ return snap_data['revision']
678+
679+
680+def is_installed(snap_name):
681+ """Return boolean indicating if snap is installed or not."""
682+ return bool(get_snap_data(snap_name))
683
684=== modified file 'ubuntu_system_tests/helpers/system_settings/app.py'
685--- ubuntu_system_tests/helpers/system_settings/app.py 2017-03-11 09:35:38 +0000
686+++ ubuntu_system_tests/helpers/system_settings/app.py 2017-03-14 11:03:55 +0000
687@@ -19,27 +19,55 @@
688 #
689 from ubuntu_system_tests.helpers import autopilot
690 from ubuntu_system_tests.helpers import processes
691-from ubuntu_system_tests.helpers.application import Application
692+from ubuntu_system_tests.helpers.application import (
693+ Deb,
694+ Snap,
695+)
696 from ubuntu_system_tests.helpers.processes import get_process_id
697 from ubuntu_system_tests.helpers.scopes.apps import (
698- launch_application_from_apps_scope)
699+ launch_application_from_apps_scope
700+)
701
702 APP = 'system-settings'
703 APP_NAME = 'System Settings'
704 APP_WIN_ID = 'ubuntu-system-settings'
705 APP_ID = 'ubuntu-system-settings'
706-APP_SNAP = 'ubuntu-system-settings'
707-
708-
709-class SystemSettings(Application):
710-
711- def __init__(self):
712- super().__init__(APP_NAME, APP_ID, APP_SNAP)
713-
714- def get_main_view(self):
715- from ubuntu_system_tests.helpers.system_settings import cpo # NOQA
716- pid = get_process_id(APP)
717- return autopilot.get_proxy_object(pid=pid).main_view
718+
719+
720+class SystemSettingsDeb(Deb):
721+
722+ def __init__(self):
723+ super().__init__(APP_NAME, APP_ID)
724+
725+ def get_main_view(self):
726+ return get_main_view()
727+
728+
729+class SystemSettingsSnap(Snap):
730+
731+ def __init__(self):
732+ super().__init__(APP_NAME, APP_ID)
733+
734+ def get_main_view(self):
735+ return get_main_view()
736+
737+
738+def get_main_view():
739+ from ubuntu_system_tests.helpers.system_settings import cpo # NOQA
740+ pid = get_process_id(APP)
741+ return autopilot.get_proxy_object(pid=pid).main_view
742+
743+
744+def get_system_settings_app(mode):
745+ """Return system settings application class of required type.
746+ :param mode: Either 'snap' or 'deb'.
747+ :return: Either SystemSettingsDeb or SystemSettingsSnap class reference.
748+ """
749+ app_modes = {
750+ 'deb': SystemSettingsDeb,
751+ 'snap': SystemSettingsSnap
752+ }
753+ return app_modes[mode]
754
755
756 def launch_system_settings(shell):
757
758=== modified file 'ubuntu_system_tests/helpers/telegram/app.py'
759--- ubuntu_system_tests/helpers/telegram/app.py 2017-02-08 15:03:35 +0000
760+++ ubuntu_system_tests/helpers/telegram/app.py 2017-03-14 11:03:55 +0000
761@@ -16,13 +16,13 @@
762 # You should have received a copy of the GNU General Public License
763 # along with this program. If not, see <http://www.gnu.org/licenses/>.
764
765-from ubuntu_system_tests.helpers.application import Application
766+from ubuntu_system_tests.helpers.application import Snap
767
768 APP_NAME = 'Telegram'
769 APP_ID = 'com.ubuntu.telegram_telegram'
770
771
772-class Telegram(Application):
773+class Telegram(Snap):
774
775 def __init__(self):
776 super().__init__(APP_NAME, APP_ID)
777
778=== modified file 'ubuntu_system_tests/helpers/terminal/app.py'
779--- ubuntu_system_tests/helpers/terminal/app.py 2017-02-09 11:43:45 +0000
780+++ ubuntu_system_tests/helpers/terminal/app.py 2017-03-14 11:03:55 +0000
781@@ -18,17 +18,18 @@
782 # along with this program. If not, see <http://www.gnu.org/licenses/>.
783 #
784
785-from ubuntu_system_tests.helpers.application import Application
786+from ubuntu_system_tests.helpers.application import Deb
787
788 APP_NAME = 'Terminal'
789-APP_ID = 'ubuntu-terminal-app'
790-APP_SNAP = 'ubuntu-terminal-app'
791-
792-
793-class Terminal(Application):
794+APP_ID = 'com.ubuntu.terminal'
795+APP_PACKAGE_ID = 'ubuntu-terminal-app'
796+
797+
798+class Terminal(Deb):
799
800 def __init__(self):
801- super().__init__(APP_NAME, APP_ID, APP_SNAP)
802+ super().__init__(APP_NAME, APP_ID, APP_PACKAGE_ID)
803
804 def get_main_view(self):
805- pass
806+ from ubuntu_system_tests.helpers.terminal import cpo # NOQA
807+ return self.get_proxy_object().main_view
808
809=== added file 'ubuntu_system_tests/helpers/terminal/cpo.py'
810--- ubuntu_system_tests/helpers/terminal/cpo.py 1970-01-01 00:00:00 +0000
811+++ ubuntu_system_tests/helpers/terminal/cpo.py 2017-03-14 11:03:55 +0000
812@@ -0,0 +1,46 @@
813+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
814+
815+# Ubuntu System Tests
816+# Copyright (C) 2017 Canonical
817+#
818+# This program is free software: you can redistribute it and/or modify
819+# it under the terms of the GNU General Public License as published by
820+# the Free Software Foundation, either version 3 of the License, or
821+# (at your option) any later version.
822+#
823+# This program is distributed in the hope that it will be useful,
824+# but WITHOUT ANY WARRANTY; without even the implied warranty of
825+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
826+# GNU General Public License for more details.
827+#
828+# You should have received a copy of the GNU General Public License
829+# along with this program. If not, see <http://www.gnu.org/licenses/>.
830+
831+from ubuntu_system_tests.helpers.autopilot import validate_dbus_object
832+from ubuntu_system_tests.helpers.ubuntuuitools.cpo.common import (
833+ CustomProxyObjectBase,
834+)
835+
836+APP_PATH_ROOT = b'comubuntuterminal'
837+
838+
839+class comubuntuterminal(CustomProxyObjectBase):
840+ """Autopilot helper object for terminal app."""
841+
842+ @classmethod
843+ def validate_dbus_object(cls, path, state):
844+ return validate_dbus_object(
845+ path, state, APP_PATH_ROOT, APP_PATH_ROOT,
846+ applicationName='com.ubuntu.terminal')
847+
848+ @property
849+ def main_view(self):
850+ return self.wait_select_single(MainView)
851+
852+
853+class MainView(CustomProxyObjectBase):
854+
855+ @classmethod
856+ def validate_dbus_object(cls, path, state):
857+ return validate_dbus_object(
858+ path, state, APP_PATH_ROOT, b'MainView', objectName='terminal')
859
860=== modified file 'ubuntu_system_tests/helpers/utils.py'
861--- ubuntu_system_tests/helpers/utils.py 2017-02-28 10:57:31 +0000
862+++ ubuntu_system_tests/helpers/utils.py 2017-03-14 11:03:55 +0000
863@@ -30,7 +30,7 @@
864 DUMMY_IMPORT_ERR = ('DummyImportObject being used because try_import failed. '
865 'Check all required dependencies are installed.')
866
867-is_discovery = None
868+discovery = None
869
870
871 def try_import(name, package=None):
872@@ -144,16 +144,16 @@
873
874 def is_discovery():
875 """Return True if being imported during test discovery."""
876- global is_discovery
877- if is_discovery is None:
878+ global discovery
879+ if discovery is None:
880 stack_trace = stack()
881 stack_trace.reverse()
882 for frame in stack_trace:
883- if frame[3] == 'load_test_suite_from_name':
884- is_discovery = True
885- return is_discovery
886- is_discovery = False
887- return is_discovery
888+ if frame[3] == 'list_tests':
889+ discovery = True
890+ return discovery
891+ discovery = False
892+ return discovery
893
894
895 class AttrDict(dict):
896
897=== modified file 'ubuntu_system_tests/helpers/webbrowser/app.py'
898--- ubuntu_system_tests/helpers/webbrowser/app.py 2017-02-08 17:02:14 +0000
899+++ ubuntu_system_tests/helpers/webbrowser/app.py 2017-03-14 11:03:55 +0000
900@@ -18,17 +18,16 @@
901 # along with this program. If not, see <http://www.gnu.org/licenses/>.
902 #
903
904-from ubuntu_system_tests.helpers.application import Application
905+from ubuntu_system_tests.helpers.application import Snap
906
907 APP_NAME = 'Browser'
908 APP_ID = 'webbrowser-app'
909-APP_SNAP = 'webbrowser-app'
910-
911-
912-class WebBrowser(Application):
913+
914+
915+class WebBrowser(Snap):
916
917 def __init__(self):
918- super().__init__(APP_NAME, APP_ID, APP_SNAP)
919+ super().__init__(APP_NAME, APP_ID)
920
921 def get_main_view(self):
922 from ubuntu_system_tests.helpers.webbrowser import cpo # NOQA
923
924=== modified file 'ubuntu_system_tests/host/target_setup.py'
925--- ubuntu_system_tests/host/target_setup.py 2017-02-20 19:49:08 +0000
926+++ ubuntu_system_tests/host/target_setup.py 2017-03-14 11:03:55 +0000
927@@ -178,6 +178,17 @@
928 ['apt-get', '-y', '--no-install-recommends', 'install'] +
929 packages)
930
931+ def install_snaps(self, snaps):
932+ if snaps:
933+ self.mount_fs_rw()
934+ for snap in snaps:
935+ cmd = 'refresh' if self.is_snap_installed(snap) else 'install'
936+ subprocess.check_call(
937+ ['snap', cmd, '--devmode', '--edge', snap])
938+
939+ def is_snap_installed(self, snap):
940+ return os.path.isdir(os.path.join('/snap', snap))
941+
942 def are_packages_installed(self, packages):
943 output = subprocess.check_output(['apt-cache', 'policy'] + packages)
944 return len(output) > 0 and 'Installed: (none)' not in output.decode()
945@@ -206,15 +217,31 @@
946 # In this case we assume running vivid based ubuntu-touch which
947 # already has unity8 running, so just return.
948 return
949- if self.config.get('package_type') == 'snap':
950- # install and setup unity8-snap-session
951+ self.setup_snapd_https_proxy()
952+ if self.config.get('unity8_mode') == 'snap':
953+ # install unity8-session snap and set as default session
954 self.install_packages(['unity8-session-snap'])
955- self.setup_snapd_https_proxy()
956 subprocess.check_call(['unity8-snap-install'])
957 session_name = 'unity8-snap'
958 else:
959- # install unity8-desktop-session
960- self.install_packages(['unity8-desktop-session'])
961+ # install deb based unity8-desktop-session and set as default.
962+ # also install ubuntu-terminal-app which is tested as deb
963+ self.install_packages([
964+ 'unity8-desktop-session',
965+ 'ubuntu-terminal-app',
966+ ])
967+ # Also install applications as snaps, which can also be launched
968+ # in a deb based session
969+ self.install_snaps([
970+ 'address-book-app',
971+ 'camera-app',
972+ 'gallery-app',
973+ 'ubuntu-calculator-app',
974+ 'ubuntu-calendar-app',
975+ 'ubuntu-clock-app',
976+ 'ubuntu-filemanager-app',
977+ 'webbrowser-app',
978+ ])
979 session_name = 'unity8'
980 self.set_unity8_default_session(session_name)
981
982
983=== modified file 'ubuntu_system_tests/host/targets.py'
984--- ubuntu_system_tests/host/targets.py 2017-03-07 11:20:22 +0000
985+++ ubuntu_system_tests/host/targets.py 2017-03-14 11:03:55 +0000
986@@ -183,7 +183,7 @@
987 'network_ready', 'update_apt', 'verbose']:
988 config[key] = getattr(args, key, False)
989 # Add options from config_stack
990- for key in ['wifi_ssid', 'wifi_password', 'package_type']:
991+ for key in ['wifi_ssid', 'wifi_password', 'unity8_mode']:
992 config[key] = config_stack.get(key)
993 # Add list of required test dependencies
994 config['test_dependencies'] = debian.get_dependencies(
995
996=== modified file 'ubuntu_system_tests/tests/base.py'
997--- ubuntu_system_tests/tests/base.py 2017-03-09 02:42:26 +0000
998+++ ubuntu_system_tests/tests/base.py 2017-03-14 11:03:55 +0000
999@@ -58,10 +58,6 @@
1000 disable_edges_intro,
1001 setup_unity
1002 )
1003-from ubuntu_system_tests.helpers.webapp import (
1004- DEFAULT_WEBVIEW_INSPECTOR_IP,
1005- DEFAULT_WEBVIEW_INSPECTOR_PORT,
1006-)
1007
1008 from ubuntu_system_tests.tests.data.definitions import DESKTOP_PLATFORM_NAME
1009
1010@@ -211,6 +207,10 @@
1011 """ Setup the environment for the webapps. This method has to be
1012 called before launch the first web app in the test.
1013 """
1014+ from ubuntu_system_tests.helpers.webapp import (
1015+ DEFAULT_WEBVIEW_INSPECTOR_IP,
1016+ DEFAULT_WEBVIEW_INSPECTOR_PORT,
1017+ )
1018 self.useFixture(JobEnvironmentVariable(
1019 global_=True,
1020 UBUNTU_WEBVIEW_DEVTOOLS_HOST=DEFAULT_WEBVIEW_INSPECTOR_IP,
1021
1022=== modified file 'ubuntu_system_tests/tests/test_launch_apps.py'
1023--- ubuntu_system_tests/tests/test_launch_apps.py 2017-02-13 14:05:03 +0000
1024+++ ubuntu_system_tests/tests/test_launch_apps.py 2017-03-14 11:03:55 +0000
1025@@ -18,399 +18,124 @@
1026 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1027 #
1028
1029-import dbus
1030 import logging
1031-import os
1032-import pwd
1033-import subprocess
1034-import tempfile
1035-import time
1036-
1037-from autopilot.testcase import AutopilotTestCase
1038-from collections import namedtuple
1039-from PIL import Image
1040-from retrying import retry
1041-from testtools.content import (
1042- attach_file,
1043- ContentType,
1044-)
1045-
1046-
1047+
1048+from ubuntu_system_tests.common.config import get_device_config_stack
1049 from ubuntu_system_tests.helpers.addressbook.app import AddressBook
1050 from ubuntu_system_tests.helpers.calculator.app import Calculator
1051+from ubuntu_system_tests.helpers.calculator.fixture_setup import (
1052+ CalculatorConfigFixture,
1053+)
1054 from ubuntu_system_tests.helpers.calendar.app import Calendar
1055 from ubuntu_system_tests.helpers.camera.app import Camera
1056 from ubuntu_system_tests.helpers.clock.app import Clock
1057 from ubuntu_system_tests.helpers.filemanager.app import FileManager
1058 from ubuntu_system_tests.helpers.gallery.app import Gallery
1059 from ubuntu_system_tests.helpers.terminal.app import Terminal
1060-from ubuntu_system_tests.helpers.system_settings.app import SystemSettings
1061+from ubuntu_system_tests.helpers.system_settings.app import (
1062+ get_system_settings_app,
1063+)
1064 from ubuntu_system_tests.helpers.webbrowser.app import WebBrowser
1065-
1066-from ubuntu_system_tests.helpers.calculator.fixture_setup import (
1067- CalculatorConfigFixture,
1068-)
1069-from ubuntu_system_tests.helpers.images import (
1070- convert_rgba_image,
1071- get_color_count,
1072- get_color_percentage,
1073- _rmsdiff as rmsdiff,
1074-)
1075-from ubuntu_system_tests.helpers.mir import get_rgba_screenshot
1076-
1077-from ubuntu_system_tests.helpers.unity8.utils import is_unity8_snap
1078-
1079-APP_LAUNCH_TIMEOUT = 30
1080-BLACK_THRESHOLD = 17
1081-PIXELS_X = '1024'
1082-PIXELS_Y = '768'
1083-RESOLUTION = '{x}x{y}'.format(x=PIXELS_X, y=PIXELS_Y)
1084-
1085-app_ids = None
1086+from ubuntu_system_tests.tests.base import BaseUbuntuSystemTestCase
1087
1088 logger = logging.getLogger(__name__)
1089 logger.setLevel(logging.INFO)
1090
1091-# TODO: Remove these AppInfo definitions when autopilot introspection is
1092-# working on snap based session.
1093-AppInfo = namedtuple('AppInfo', ['app_class', 'color_stats_list'])
1094-APP_INFO_ADDRESSBOOK = AppInfo(AddressBook, [(8, 200, 400)])
1095-APP_INFO_CAMERA = AppInfo(Camera, [
1096- (16, 1700, 2000), # Full screen
1097- (16, 28000, 31000)]) # Low space warning
1098-APP_INFO_GALLERY = AppInfo(Gallery, [(10, 200, 400)])
1099-APP_INFO_CALCULATOR = AppInfo(Calculator, [(8, 4400, 4800)])
1100-APP_INFO_CALENDAR = AppInfo(Calendar, [(8, 2300, 2700)])
1101-APP_INFO_CLOCK = AppInfo(Clock, [
1102- (8, 300, 600), # Full screen
1103- (8, 4000, 4500)]) # Right hand side dock
1104-APP_INFO_FILEMANAGER = AppInfo(FileManager, [(8, 1900, 2200)])
1105-APP_INFO_TERMINAL = AppInfo(Terminal, [(85, 600, 900)])
1106-APP_INFO_WEBBROWSER = AppInfo(WebBrowser, [(8, 2000, 2300)])
1107-APP_INFO_SETTINGS = AppInfo(SystemSettings, [
1108- (5, 200, 600), # Full screen
1109- (5, 2100, 2500), # Left hand side
1110- (5, 2800, 3100)]) # Left hand side, compacted
1111-
1112-unity8_snap = None
1113-
1114-
1115-# TODO: Remove and replace with SetupUnity8 fixture when working in snap based
1116-# session.
1117-def restart_unity8():
1118- """Restart unity8 process."""
1119- # Ensure unity8 is running in first place
1120- wait_for_unity8_running()
1121- # kill process and check that unity8 restarts by itself
1122- subprocess.check_call(['pkill', 'unity8'])
1123- # Allow screen to go black before waiting for it to run again
1124- time.sleep(10)
1125- wait_for_unity8_running()
1126-
1127-
1128-def wait_for_unity8_running():
1129- """Wait until unity8 is displaying on screen."""
1130- for c in range(60):
1131- screen = get_screenshot()
1132- black = get_color_percentage(screen, BLACK_THRESHOLD)
1133- count = get_color_count(screen)
1134- logger.info('#### BLACK :: {} :: COLORS :: {}'.format(black, count))
1135- os.remove(screen)
1136- if black < 50 and count > 100:
1137- # Black screen is no longer displayed
1138- return
1139- time.sleep(1)
1140- raise RuntimeError('Unity8 UI is not displayed!')
1141-
1142-
1143-def get_command(command):
1144- """Return command based on whether unity8 is running from a snap or not.
1145- If it is then prefix command with 'unity8-session', otherwise return
1146- command with no prefix.
1147- """
1148- global unity8_snap
1149- if unity8_snap is None:
1150- unity8_snap = is_unity8_snap()
1151- if unity8_snap:
1152- return 'unity8-session.{}'.format(command)
1153- return command
1154-
1155-
1156-# TODO: Remove all below screenshot analysis when autopilot introspection
1157-# working on snap based session.
1158-def get_screenshot():
1159- """
1160- Take screen shot and return file path of png file
1161- :return: Path of PNG screen image file
1162- """
1163- timestamp = int(time.time())
1164- filename_rgba = 'screenshot-{}.rgba'.format(timestamp)
1165- filename_png = 'screenshot-{}.png'.format(timestamp)
1166- temp_dir = tempfile.gettempdir()
1167- filepath_rgba = os.path.join(temp_dir, filename_rgba)
1168- filepath_png = os.path.join(temp_dir, filename_png)
1169- get_rgba_screenshot(filepath_rgba)
1170- convert_rgba_image(filepath_rgba, filepath_png, RESOLUTION)
1171- # finished with rgba file so delete it
1172- os.remove(filepath_rgba)
1173- return filepath_png
1174-
1175-
1176-def images_almost_equal(im1, im2):
1177- """ Determine if two images are almost equal or not
1178- :return: True if the images have almost same content, False otherwise
1179- """
1180- try:
1181- difference = rmsdiff(im1, im2)
1182- except ValueError:
1183- logger.info('### Images have no similarity.')
1184- return False
1185- logger.info('### Image difference :: {}'.format(difference))
1186- return difference < 4
1187-
1188-
1189-def image_files_almost_equal(im1_path, im2_path):
1190- """ Determine if two image files are equal or not.
1191- :param im1_path: File path to first image file.
1192- :param im2_path: File path to second image file.
1193- :return: True if the image files have same content, False otherwise.
1194- """
1195- return images_almost_equal(Image.open(im1_path), Image.open(im2_path))
1196-
1197-
1198-# TODO: Remove this when Tutorial and SettingsWizard fixtures working.
1199-def get_user_account():
1200- for account in sorted(pwd.getpwall(), key=lambda x: x.pw_uid):
1201- if account.pw_uid > 500 and account.pw_dir.startswith('/home/'):
1202- return account
1203-
1204-
1205-# TODO: Remove this and replace with Tutorial fixture when working.
1206-def disable_edges_demo():
1207- # Getting or setting the dbus property must be done from user account,
1208- # so use dbus-send with sudo -u to set the user.
1209- setting_changed = False
1210- user_id = 'User{}'.format(get_user_account().pw_uid)
1211- wait_for_user(user_id)
1212- user_path = '/org/freedesktop/Accounts/{}'.format(user_id)
1213- user_name = get_user_account().pw_name
1214- if get_edge_demo_status(user_name, user_path):
1215- subprocess.check_call([
1216- 'sudo', '-u', user_name,
1217- 'dbus-send', '--system', '--print-reply',
1218- '--dest=org.freedesktop.Accounts', user_path,
1219- 'org.freedesktop.DBus.Properties.Set',
1220- 'string:com.canonical.unity.AccountsService',
1221- 'string:demo-edges', 'variant:boolean:false'],
1222- stdout=subprocess.DEVNULL)
1223- setting_changed = True
1224- return setting_changed
1225-
1226-
1227-@retry(wait_fixed=1000,
1228- stop_max_attempt_number=30,
1229- retry_on_exception=lambda e: (
1230- isinstance(e, subprocess.CalledProcessError)))
1231-def get_edge_demo_status(user_name, user_path):
1232- """Return edge demo boolean status."""
1233- output = subprocess.check_output([
1234- 'sudo', '-u', user_name,
1235- 'dbus-send', '--system', '--print-reply',
1236- '--dest=org.freedesktop.Accounts', user_path,
1237- 'org.freedesktop.DBus.Properties.Get',
1238- 'string:com.canonical.unity.AccountsService',
1239- 'string:demo-edges']).decode().strip().split('\n')[-1]
1240- return output.split(' ')[-1] == 'true'
1241-
1242-
1243-@retry(wait_fixed=1000,
1244- stop_max_attempt_number=30,
1245- retry_on_exception=lambda e: isinstance(e, RuntimeError))
1246-def wait_for_user(user_id):
1247- """Wait until user_id object exists under Accounts object."""
1248- bus = dbus.SystemBus()
1249- obj = bus.get_object(
1250- 'org.freedesktop.Accounts', '/org/freedesktop/Accounts')
1251- intf = dbus.Interface(obj, 'org.freedesktop.DBus.Introspectable')
1252- if not 'name="{}"'.format(user_id) in intf.Introspect():
1253- raise RuntimeError('User {} does not exist.'.format(user_id))
1254-
1255-
1256-# TODO: Remove this and replace with SettingsWizard fixture when working.
1257-def disable_settings_wizard():
1258- """Disable settings wizard from running on first boot. Return bool
1259- indicating if setting was changed."""
1260- setting_changed = False
1261- base_path = '/home/{}'.format(get_user_account().pw_name)
1262- if is_unity8_snap():
1263- base_path = os.path.join(base_path, 'snap/unity8-session/common')
1264- wizard_file = os.path.join(
1265- base_path, '.config/ubuntu-system-settings/wizard-has-run')
1266- if not os.path.isfile(wizard_file):
1267- wizard_dir = os.path.dirname(wizard_file)
1268- if not os.path.isdir(wizard_dir):
1269- os.makedirs(wizard_dir)
1270- open(wizard_file, 'a').close()
1271- setting_changed = True
1272- return setting_changed
1273-
1274-
1275-# TODO: Change to BaseUbuntuSystemTestCase when it works on snap based session.
1276-class AppLaunchBase(AutopilotTestCase):
1277-
1278- @classmethod
1279- def setUpClass(cls):
1280- wizard_changed = disable_settings_wizard()
1281- edges_changed = disable_edges_demo()
1282- if wizard_changed or edges_changed:
1283- restart_unity8()
1284+
1285+class AppLaunchBase(BaseUbuntuSystemTestCase):
1286
1287 def setUp(self):
1288 super().setUp()
1289 self.app = None
1290- self.before = None
1291- self.after = None
1292- wait_for_unity8_running()
1293+ self.config_stack = get_device_config_stack()
1294
1295 def tearDown(self):
1296 if self.app:
1297- self.close_app()
1298- if self.before and os.path.isfile(self.before):
1299- os.remove(self.before)
1300- self.before = None
1301- if self.after and os.path.isfile(self.after):
1302- os.remove(self.after)
1303- self.after = None
1304+ self.app.stop()
1305 super().tearDown()
1306
1307- def close_app(self):
1308- self.app.stop()
1309- time.sleep(10)
1310-
1311- def maximise_window(self):
1312- self.keyboard.press_and_release('Ctrl+LeftMeta+Up')
1313- time.sleep(10)
1314-
1315- def check_app_launch(self, app_info, launch_count):
1316- self.app = app_info.app_class()
1317- logger.info('### TESTING APP NAME :: {}'.format(self.app.app_name))
1318- self.maximise_window()
1319+ def check_app_launch(self, app_class, launch_count):
1320+ self.app = app_class()
1321+ if not self.app.is_installed():
1322+ reason = '{n} not installed as {t}'.format(
1323+ n=self.app.app_id, t=self.app.app_type)
1324+ self.app = None
1325+ self.skipTest(reason)
1326 for count in range(1, launch_count + 1):
1327- logger.info('### APP LAUNCH :: {}'.format(count))
1328- # TODO: Replace all screenshot analysis with introspection when
1329- # autopilot working on snap based session.
1330- self.before = get_screenshot()
1331- self.attach_screenshot(
1332- self.before, 'before_launched_{}'.format(count))
1333 self.app.launch()
1334- time.sleep(10)
1335- self.maximise_window()
1336- self.after = get_screenshot()
1337- self.attach_screenshot(
1338- self.after, 'after_launched_{}'.format(count))
1339+ self.assertIsNotNone(self.app.get_main_view())
1340+ logger.info('App launch {c} successful'.format(c=count))
1341 if count < launch_count:
1342 # Only close the app here if this is not the last iteration.
1343 # Otherwise it will be closed automatically in tearDown.
1344- self.close_app()
1345- black = get_color_percentage(self.after, BLACK_THRESHOLD)
1346- count = get_color_count(self.after)
1347- logger.info(
1348- '### ID {} :: BLACK {} :: COLORS '
1349- '{}'.format(self.app.app_id, black, count, self.before,
1350- self.after))
1351- self.assertFalse(
1352- image_files_almost_equal(self.before, self.after),
1353- 'No difference on screen detected after app launched.')
1354- self.validate_color_stats(app_info, count, black)
1355-
1356- def validate_color_stats(self, app_info, color_count, black_percent):
1357- exceptions = []
1358- for stats in app_info.color_stats_list:
1359- black_max = stats[0]
1360- colors_min = stats[1]
1361- colors_max = stats[2]
1362- try:
1363- self.assertGreater(color_count, colors_min)
1364- self.assertLess(color_count, colors_max)
1365- self.assertLess(black_percent, black_max)
1366- except AssertionError as e:
1367- exceptions.append(e)
1368- else:
1369- break
1370- if len(exceptions) == len(app_info.color_stats_list):
1371- # An exception was raised for every stats item, which means
1372- # that none of them matched. So raise the first exception here.
1373- raise exceptions[0]
1374-
1375- def attach_screenshot(self, file_path, name):
1376- attach_file(
1377- self, file_path, name=name,
1378- content_type=ContentType('image', 'png'), buffer_now=True)
1379+ self.app.stop()
1380
1381
1382 class LaunchAppOnceTestCase(AppLaunchBase):
1383
1384 def test_launch_addressbook_app(self):
1385- self.check_app_launch(APP_INFO_ADDRESSBOOK, 1)
1386+ self.check_app_launch(AddressBook, 1)
1387
1388 def test_launch_camera_app(self):
1389- self.check_app_launch(APP_INFO_CAMERA, 1)
1390+ self.check_app_launch(Camera, 1)
1391
1392 def test_launch_gallery_app(self):
1393- self.check_app_launch(APP_INFO_GALLERY, 1)
1394+ self.check_app_launch(Gallery, 1)
1395
1396 def test_launch_calculator_app(self):
1397 self.useFixture(CalculatorConfigFixture(first_run=False))
1398- self.check_app_launch(APP_INFO_CALCULATOR, 1)
1399+ self.check_app_launch(Calculator, 1)
1400
1401 def test_launch_calendar_app(self):
1402- self.check_app_launch(APP_INFO_CALENDAR, 1)
1403+ self.check_app_launch(Calendar, 1)
1404
1405 def test_launch_clock_app(self):
1406- self.check_app_launch(APP_INFO_CLOCK, 1)
1407+ self.check_app_launch(Clock, 1)
1408
1409 def test_launch_filemanager_app(self):
1410- self.check_app_launch(APP_INFO_FILEMANAGER, 1)
1411+ self.check_app_launch(FileManager, 1)
1412
1413 def test_launch_terminal_app(self):
1414- self.check_app_launch(APP_INFO_TERMINAL, 1)
1415+ self.check_app_launch(Terminal, 1)
1416
1417 def test_launch_webbrowser_app(self):
1418- self.check_app_launch(APP_INFO_WEBBROWSER, 1)
1419+ self.check_app_launch(WebBrowser, 1)
1420
1421 def test_launch_systemsettings_app(self):
1422- self.check_app_launch(APP_INFO_SETTINGS, 1)
1423+ app = get_system_settings_app(self.config_stack.get('unity8_mode'))
1424+ self.check_app_launch(app, 1)
1425
1426
1427 class LaunchAppTwiceTestCase(AppLaunchBase):
1428
1429 def test_launch_addressbook_app_twice(self):
1430- self.check_app_launch(APP_INFO_ADDRESSBOOK, 2)
1431+ self.check_app_launch(AddressBook, 2)
1432
1433 def test_launch_camera_app_twice(self):
1434- self.check_app_launch(APP_INFO_CAMERA, 2)
1435+ self.check_app_launch(Camera, 2)
1436
1437 def test_launch_gallery_app_twice(self):
1438- self.check_app_launch(APP_INFO_GALLERY, 2)
1439+ self.check_app_launch(Gallery, 2)
1440
1441 def test_launch_calculator_app_twice(self):
1442 self.useFixture(CalculatorConfigFixture(first_run=False))
1443- self.check_app_launch(APP_INFO_CALCULATOR, 2)
1444+ self.check_app_launch(Calculator, 2)
1445
1446 def test_launch_calendar_app_twice(self):
1447- self.check_app_launch(APP_INFO_CALENDAR, 2)
1448+ self.check_app_launch(Calendar, 2)
1449
1450 def test_launch_clock_app_twice(self):
1451- self.check_app_launch(APP_INFO_CLOCK, 2)
1452+ self.check_app_launch(Clock, 2)
1453
1454 def test_launch_filemanager_app_twice(self):
1455- self.check_app_launch(APP_INFO_FILEMANAGER, 2)
1456+ self.check_app_launch(FileManager, 2)
1457
1458 def test_launch_terminal_app_twice(self):
1459- self.check_app_launch(APP_INFO_TERMINAL, 2)
1460+ self.check_app_launch(Terminal, 2)
1461
1462 def test_launch_webbrowser_app_twice(self):
1463- self.check_app_launch(APP_INFO_WEBBROWSER, 2)
1464+ self.check_app_launch(WebBrowser, 2)
1465
1466 def test_launch_systemsettings_app_twice(self):
1467- self.check_app_launch(APP_INFO_SETTINGS, 2)
1468+ app = get_system_settings_app(self.config_stack.get('unity8_mode'))
1469+ self.check_app_launch(app, 2)
1470
1471=== modified file 'ubuntu_system_tests/tests/test_system_settings.py'
1472--- ubuntu_system_tests/tests/test_system_settings.py 2017-03-08 01:00:05 +0000
1473+++ ubuntu_system_tests/tests/test_system_settings.py 2017-03-14 11:03:55 +0000
1474@@ -20,6 +20,8 @@
1475
1476 from autopilot.matchers import Eventually
1477 from testtools.matchers import Equals
1478+
1479+from ubuntu_system_tests.common.config import get_device_config_stack
1480 from ubuntu_system_tests.helpers.network.utils import turn_on_wifi
1481
1482 from ubuntu_system_tests.helpers.indicators.network.cpo import (
1483@@ -28,7 +30,9 @@
1484 from ubuntu_system_tests.helpers import network
1485 from ubuntu_system_tests.helpers import system_settings
1486 from ubuntu_system_tests.helpers.indicators.indicators import IndicatorsFactory
1487-from ubuntu_system_tests.helpers.system_settings.app import SystemSettings
1488+from ubuntu_system_tests.helpers.system_settings.app import (
1489+ get_system_settings_app,
1490+)
1491 from ubuntu_system_tests.tests import base
1492
1493 SOUND_SETTINGS_PAGE = 'soundPage'
1494@@ -76,7 +80,9 @@
1495
1496 def setUp(self):
1497 super().setUp()
1498- system_settings_app = SystemSettings()
1499+ self.config_stack = get_device_config_stack()
1500+ system_settings_app = get_system_settings_app(
1501+ self.config_stack.get('unity8_mode'))()
1502 system_settings_app.launch()
1503 self.system_settings = system_settings_app.get_main_view()
1504

Subscribers

People subscribed via source and target branches

to all changes: