Merge lp:~larryprice/libertine/new-list-apps into lp:libertine
- new-list-apps
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Christopher Townsend |
Approved revision: | 393 |
Merged at revision: | 392 |
Proposed branch: | lp:~larryprice/libertine/new-list-apps |
Merge into: | lp:libertine |
Diff against target: |
654 lines (+20/-438) 16 files modified
debian/python3-libertine.install (+0/-1) python/libertine/AppDiscovery.py (+0/-220) python/libertine/Libertine.py (+0/-18) python/libertine/service/container.py (+0/-9) python/libertine/service/manager.py (+0/-7) python/libertine/service/task_dispatcher.py (+0/-4) python/libertine/service/tasks/__init__.py (+0/-2) python/libertine/service/tasks/list_apps_task.py (+0/-34) tests/integration/test_libertine_service.py (+1/-2) tests/unit/service/tasks/CMakeLists.txt (+0/-1) tests/unit/service/tasks/test_list_app_ids_task.py (+1/-1) tests/unit/service/tasks/test_list_apps_task.py (+0/-59) tests/unit/service/test_container.py (+3/-11) tests/unit/service/test_task_dispatcher.py (+7/-14) tests/unit/test_app_discovery.py (+0/-52) tools/libertine-container-manager (+8/-3) |
To merge this branch: | bzr merge lp:~larryprice/libertine/new-list-apps |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Christopher Townsend | Approve | ||
Libertine CI Bot | continuous-integration | Approve | |
Review via email: mp+316556@code.launchpad.net |
Commit message
Migrate the list-apps subcommand to list application ids.
Description of the change
Migrate the list-apps subcommand to list application ids.
Libertine CI Bot (libertine-ci-bot) wrote : | # |
- 392. By Larry Price
-
missed a spot
Libertine CI Bot (libertine-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:392
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 393. By Larry Price
-
test method was using wrong dbus service giving false positive
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:393
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Preview Diff
1 | === modified file 'debian/python3-libertine.install' | |||
2 | --- debian/python3-libertine.install 2017-01-31 18:21:43 +0000 | |||
3 | +++ debian/python3-libertine.install 2017-02-07 13:30:52 +0000 | |||
4 | @@ -1,4 +1,3 @@ | |||
5 | 1 | usr/lib/python*/*/libertine/AppDiscovery.py | ||
6 | 2 | usr/lib/python*/*/libertine/ContainersConfig.py | 1 | usr/lib/python*/*/libertine/ContainersConfig.py |
7 | 3 | usr/lib/python*/*/libertine/HostInfo.py | 2 | usr/lib/python*/*/libertine/HostInfo.py |
8 | 4 | usr/lib/python*/*/libertine/Libertine.py | 3 | usr/lib/python*/*/libertine/Libertine.py |
9 | 5 | 4 | ||
10 | === removed file 'python/libertine/AppDiscovery.py' | |||
11 | --- python/libertine/AppDiscovery.py 2017-01-17 21:46:43 +0000 | |||
12 | +++ python/libertine/AppDiscovery.py 1970-01-01 00:00:00 +0000 | |||
13 | @@ -1,220 +0,0 @@ | |||
14 | 1 | # Copyright 2015 Canonical Ltd. | ||
15 | 2 | # | ||
16 | 3 | # This program is free software: you can redistribute it and/or modify it | ||
17 | 4 | # under the terms of the GNU General Public License version 3, as published | ||
18 | 5 | # by the Free Software Foundation. | ||
19 | 6 | # | ||
20 | 7 | # This program is distributed in the hope that it will be useful, but | ||
21 | 8 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
22 | 9 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
23 | 10 | # PURPOSE. See the GNU General Public License for more details. | ||
24 | 11 | # | ||
25 | 12 | # You should have received a copy of the GNU General Public License along | ||
26 | 13 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
27 | 14 | |||
28 | 15 | from . import utils | ||
29 | 16 | from xdg.BaseDirectory import xdg_data_dirs | ||
30 | 17 | import configparser | ||
31 | 18 | import glob | ||
32 | 19 | import io | ||
33 | 20 | import json | ||
34 | 21 | import os | ||
35 | 22 | import re | ||
36 | 23 | import sys | ||
37 | 24 | |||
38 | 25 | |||
39 | 26 | class IconCache(object): | ||
40 | 27 | """ | ||
41 | 28 | Caches the names of all icon files available in the standard places (possibly | ||
42 | 29 | in a container) and provides a search function to deliver icon file names | ||
43 | 30 | matching a given icon name. | ||
44 | 31 | |||
45 | 32 | See the `Freedesktop.org icon theme specification | ||
46 | 33 | <http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html>`_ | ||
47 | 34 | for the detailed specification. | ||
48 | 35 | """ | ||
49 | 36 | |||
50 | 37 | def __init__(self, root_path, file_loader=None): | ||
51 | 38 | """ | ||
52 | 39 | :param root_path: Where to start the file scan. | ||
53 | 40 | :type root_path: A valid filesystem path string. | ||
54 | 41 | :param file_loader: A function that builds a cache of filenames. | ||
55 | 42 | :type file_loader: Function returning a list of filenames. | ||
56 | 43 | """ | ||
57 | 44 | if file_loader: | ||
58 | 45 | self._icon_cache = file_loader(root_path) | ||
59 | 46 | else: | ||
60 | 47 | self._icon_cache = self._file_loader(root_path) | ||
61 | 48 | |||
62 | 49 | def _get_icon_search_paths(self, root_path): | ||
63 | 50 | """ | ||
64 | 51 | Gets a list of paths on which to search for icons. | ||
65 | 52 | |||
66 | 53 | :param root_path: Where to start the file scan. | ||
67 | 54 | :type root_path: A valid filesystem path string. | ||
68 | 55 | :rtype: A list of filesystem patch to serarch for qualifying icon files. | ||
69 | 56 | """ | ||
70 | 57 | icon_search_paths = [] | ||
71 | 58 | icon_search_paths.append(os.path.join(root_path, os.environ['HOME'], ".icons")) | ||
72 | 59 | for d in reversed(xdg_data_dirs): | ||
73 | 60 | icon_search_paths.append(os.path.join(root_path, d.lstrip('/'), "icons")) | ||
74 | 61 | icon_search_paths.append(os.path.join(root_path, "usr/share/pixmaps")) | ||
75 | 62 | return icon_search_paths | ||
76 | 63 | |||
77 | 64 | def _file_loader(self, root_path): | ||
78 | 65 | """ | ||
79 | 66 | Loads a cache of file names by scanning the filesystem rooted at | ||
80 | 67 | ``root_path``. | ||
81 | 68 | |||
82 | 69 | :param root_path: Where to start the file scan. | ||
83 | 70 | :type root_path: A valid filesystem path. | ||
84 | 71 | :rtype: A list of fully-qualified file paths. | ||
85 | 72 | """ | ||
86 | 73 | file_names = [] | ||
87 | 74 | pattern = re.compile(r".*\.(png|svg|xpm)") | ||
88 | 75 | for path in self._get_icon_search_paths(root_path): | ||
89 | 76 | for base, dirs, files in os.walk(path): | ||
90 | 77 | for file in files: | ||
91 | 78 | if pattern.match(file): | ||
92 | 79 | file_names.append(os.path.join(base, file)) | ||
93 | 80 | return file_names | ||
94 | 81 | |||
95 | 82 | def _find_icon_files(self, icon_name): | ||
96 | 83 | """ | ||
97 | 84 | Finds a list of file name strings matching the given icon name. | ||
98 | 85 | |||
99 | 86 | :param icon_name: An icon name, pobably from a .desktop file. | ||
100 | 87 | :rtype: A list of filename strings matching the icon name. | ||
101 | 88 | """ | ||
102 | 89 | icon_file_names = [] | ||
103 | 90 | match_string = r"/" + os.path.splitext(icon_name)[0] + r"\....$" | ||
104 | 91 | pattern = re.compile(match_string) | ||
105 | 92 | for icon_file in self._icon_cache: | ||
106 | 93 | if pattern.search(icon_file): | ||
107 | 94 | icon_file_names.append(icon_file) | ||
108 | 95 | return icon_file_names | ||
109 | 96 | |||
110 | 97 | def expand_icons(self, desktop_icon_list): | ||
111 | 98 | """ | ||
112 | 99 | Expands a string containing a list of icon names into a list of matching | ||
113 | 100 | file names. | ||
114 | 101 | |||
115 | 102 | :param desktop_icon_list: A string containing a list of icon names | ||
116 | 103 | separated by semicolons. | ||
117 | 104 | :rtype: A list of filename stings matching the icon names passed in. | ||
118 | 105 | |||
119 | 106 | See `the Freedesktop.org desktop file specification | ||
120 | 107 | <http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#recognized-keys>`_ | ||
121 | 108 | for more information. | ||
122 | 109 | """ | ||
123 | 110 | if desktop_icon_list: | ||
124 | 111 | icon_list = desktop_icon_list.split(';') | ||
125 | 112 | if icon_list[0][0] == '/': | ||
126 | 113 | return icon_list | ||
127 | 114 | icon_files = [] | ||
128 | 115 | for i in icon_list: | ||
129 | 116 | icon_files += self._find_icon_files(i) | ||
130 | 117 | return icon_files | ||
131 | 118 | return [] | ||
132 | 119 | |||
133 | 120 | |||
134 | 121 | def expand_mime_types(desktop_mime_types): | ||
135 | 122 | if desktop_mime_types: | ||
136 | 123 | return desktop_mime_types.split(';') | ||
137 | 124 | return [] | ||
138 | 125 | |||
139 | 126 | |||
140 | 127 | class AppInfo(object): | ||
141 | 128 | |||
142 | 129 | def __init__(self, desktop_file_name, config_entry, icon_cache): | ||
143 | 130 | self.desktop_file_name = desktop_file_name | ||
144 | 131 | self.name = config_entry.get('Name') | ||
145 | 132 | if not self.name: | ||
146 | 133 | raise RuntimeError("required Name attribute is missing") | ||
147 | 134 | d = config_entry.get('NoDisplay') | ||
148 | 135 | self.no_display = (d != None and d == 'true') | ||
149 | 136 | self.exec_line = config_entry.get('Exec') | ||
150 | 137 | if not self.exec_line: | ||
151 | 138 | raise RuntimeError("required Exec attribute is missing") | ||
152 | 139 | self.icons = icon_cache.expand_icons(config_entry.get('Icon')) | ||
153 | 140 | self.mime_types = expand_mime_types(config_entry.get('MimeType')) | ||
154 | 141 | |||
155 | 142 | def __str__(self): | ||
156 | 143 | with io.StringIO() as ostr: | ||
157 | 144 | print(self.name, file=ostr) | ||
158 | 145 | print(" desktop_file={}".format(self.desktop_file_name), file=ostr) | ||
159 | 146 | print(" no-display={}".format(self.no_display), file=ostr) | ||
160 | 147 | print(" exec='{}'".format(self.exec_line), file=ostr) | ||
161 | 148 | for icon in self.icons: | ||
162 | 149 | print(" icon: {}".format(icon), file=ostr) | ||
163 | 150 | for mime in self.mime_types: | ||
164 | 151 | print(" mime: {}".format(mime), file=ostr) | ||
165 | 152 | return ostr.getvalue() | ||
166 | 153 | |||
167 | 154 | def to_json(self): | ||
168 | 155 | return json.dumps(self.__dict__) | ||
169 | 156 | |||
170 | 157 | |||
171 | 158 | def desktop_file_is_showable(desktop_entry): | ||
172 | 159 | """ | ||
173 | 160 | Determines if a particular application entry should be reported: the entry | ||
174 | 161 | can not be hidden and must be showable in Unity. | ||
175 | 162 | """ | ||
176 | 163 | t = desktop_entry.get('Type') | ||
177 | 164 | if t != 'Application': | ||
178 | 165 | return False | ||
179 | 166 | n = desktop_entry.get('Hidden') | ||
180 | 167 | if n and n == 'true': | ||
181 | 168 | return False | ||
182 | 169 | n = desktop_entry.get('NoShowIn') | ||
183 | 170 | if n: | ||
184 | 171 | targets = n.split(';') | ||
185 | 172 | if 'Unity' in targets: | ||
186 | 173 | return False | ||
187 | 174 | n = desktop_entry.get('OnlyShowIn') | ||
188 | 175 | if n: | ||
189 | 176 | targets = n.split(';') | ||
190 | 177 | if 'Unity' not in targets: | ||
191 | 178 | return False | ||
192 | 179 | return True | ||
193 | 180 | |||
194 | 181 | |||
195 | 182 | def get_app_info(desktop_path, icon_cache): | ||
196 | 183 | for desktop_file_name in glob.glob(desktop_path): | ||
197 | 184 | desktop_file = configparser.ConfigParser(strict=False, interpolation=None) | ||
198 | 185 | try: | ||
199 | 186 | desktop_file.read(desktop_file_name) | ||
200 | 187 | desktop_entry = desktop_file['Desktop Entry'] | ||
201 | 188 | if desktop_file_is_showable(desktop_entry): | ||
202 | 189 | yield AppInfo(desktop_file_name, desktop_entry, icon_cache) | ||
203 | 190 | except Exception as ex: | ||
204 | 191 | utils.get_logger().error("error processing {}: {}".format(desktop_file_name, ex)) | ||
205 | 192 | |||
206 | 193 | |||
207 | 194 | class AppLauncherCache(object): | ||
208 | 195 | """ | ||
209 | 196 | Caches a list of application launcher information (derived from .desktop | ||
210 | 197 | files installed in a container. | ||
211 | 198 | """ | ||
212 | 199 | |||
213 | 200 | def __init__(self, name, root_path): | ||
214 | 201 | self.name = name | ||
215 | 202 | self.app_launchers = [] | ||
216 | 203 | icon_cache = IconCache(root_path) | ||
217 | 204 | for dir in reversed(xdg_data_dirs): | ||
218 | 205 | path = os.path.join(root_path, dir.lstrip('/'), "applications") | ||
219 | 206 | for app_info in get_app_info(os.path.join(path, "*.desktop"), icon_cache): | ||
220 | 207 | self.app_launchers.append(app_info) | ||
221 | 208 | |||
222 | 209 | def __str__(self): | ||
223 | 210 | with io.StringIO() as ostr: | ||
224 | 211 | print("{}\n".format(self.name), file=ostr) | ||
225 | 212 | for app_info in self.app_launchers: | ||
226 | 213 | print(" {}".format(app_info), file=ostr) | ||
227 | 214 | return ostr.getvalue() | ||
228 | 215 | |||
229 | 216 | def to_json(self): | ||
230 | 217 | return json.dumps(self, | ||
231 | 218 | default=lambda o: o.__dict__, | ||
232 | 219 | sort_keys=True, | ||
233 | 220 | indent=4) | ||
234 | 221 | 0 | ||
235 | === modified file 'python/libertine/Libertine.py' | |||
236 | --- python/libertine/Libertine.py 2017-02-01 21:13:14 +0000 | |||
237 | +++ python/libertine/Libertine.py 2017-02-07 13:30:52 +0000 | |||
238 | @@ -12,7 +12,6 @@ | |||
239 | 12 | # You should have received a copy of the GNU General Public License along | 12 | # You should have received a copy of the GNU General Public License along |
240 | 13 | # with this program. If not, see <http://www.gnu.org/licenses/>. | 13 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
241 | 14 | 14 | ||
242 | 15 | from .AppDiscovery import AppLauncherCache | ||
243 | 16 | import abc | 15 | import abc |
244 | 17 | import contextlib | 16 | import contextlib |
245 | 18 | import os | 17 | import os |
246 | @@ -525,23 +524,6 @@ | |||
247 | 525 | """ | 524 | """ |
248 | 526 | self.container.finish_application(app) | 525 | self.container.finish_application(app) |
249 | 527 | 526 | ||
250 | 528 | def list_app_launchers(self, use_json=False): | ||
251 | 529 | """ | ||
252 | 530 | Enumerates all application launchers (based on .desktop files) available | ||
253 | 531 | in the container. | ||
254 | 532 | |||
255 | 533 | :param use_json: Indicates the returned string should be in JSON format. | ||
256 | 534 | The default format is some human-readble format. | ||
257 | 535 | :rtype: A printable string containing a list of application launchers | ||
258 | 536 | available in the container. | ||
259 | 537 | """ | ||
260 | 538 | if use_json: | ||
261 | 539 | return AppLauncherCache(self.container.name, | ||
262 | 540 | self.container.root_path).to_json() | ||
263 | 541 | else: | ||
264 | 542 | return str(AppLauncherCache(self.container.name, | ||
265 | 543 | self.container.root_path)) | ||
266 | 544 | |||
267 | 545 | def list_app_ids(self): | 527 | def list_app_ids(self): |
268 | 546 | """ | 528 | """ |
269 | 547 | Finds application ids (based on .desktop files) available in the | 529 | Finds application ids (based on .desktop files) available in the |
270 | 548 | 530 | ||
271 | === modified file 'python/libertine/service/container.py' | |||
272 | --- python/libertine/service/container.py 2017-01-24 16:03:25 +0000 | |||
273 | +++ python/libertine/service/container.py 2017-02-07 13:30:52 +0000 | |||
274 | @@ -144,15 +144,6 @@ | |||
275 | 144 | task.start() | 144 | task.start() |
276 | 145 | return task.id | 145 | return task.id |
277 | 146 | 146 | ||
278 | 147 | def list_apps(self): | ||
279 | 148 | utils.get_logger().debug("List all apps in container '%s'" % self.id) | ||
280 | 149 | |||
281 | 150 | task = ListAppsTask(self.id, self._config, self._connection, self._cleanup_task) | ||
282 | 151 | |||
283 | 152 | self._tasks.append(task) | ||
284 | 153 | task.start() | ||
285 | 154 | return task.id | ||
286 | 155 | |||
287 | 156 | def list_app_ids(self): | 147 | def list_app_ids(self): |
288 | 157 | utils.get_logger().debug("List all app ids in container '%s'" % self.id) | 148 | utils.get_logger().debug("List all app ids in container '%s'" % self.id) |
289 | 158 | 149 | ||
290 | 159 | 150 | ||
291 | === modified file 'python/libertine/service/manager.py' | |||
292 | --- python/libertine/service/manager.py 2017-01-31 19:24:32 +0000 | |||
293 | +++ python/libertine/service/manager.py 2017-02-07 13:30:52 +0000 | |||
294 | @@ -69,13 +69,6 @@ | |||
295 | 69 | @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, | 69 | @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, |
296 | 70 | in_signature='s', | 70 | in_signature='s', |
297 | 71 | out_signature='o') | 71 | out_signature='o') |
298 | 72 | def list_apps(self, container_id): | ||
299 | 73 | utils.get_logger().debug("list_apps('{}')".format(container_id)) | ||
300 | 74 | return self._dispatcher.list_apps(container_id) | ||
301 | 75 | |||
302 | 76 | @dbus.service.method(LIBERTINE_MANAGER_INTERFACE, | ||
303 | 77 | in_signature='s', | ||
304 | 78 | out_signature='o') | ||
305 | 79 | def list_app_ids(self, container_id): | 72 | def list_app_ids(self, container_id): |
306 | 80 | utils.get_logger().debug("list_app_ids('{}')".format(container_id)) | 73 | utils.get_logger().debug("list_app_ids('{}')".format(container_id)) |
307 | 81 | return self._dispatcher.list_app_ids(container_id) | 74 | return self._dispatcher.list_app_ids(container_id) |
308 | 82 | 75 | ||
309 | === modified file 'python/libertine/service/task_dispatcher.py' | |||
310 | --- python/libertine/service/task_dispatcher.py 2017-01-24 18:00:57 +0000 | |||
311 | +++ python/libertine/service/task_dispatcher.py 2017-02-07 13:30:52 +0000 | |||
312 | @@ -84,10 +84,6 @@ | |||
313 | 84 | utils.get_logger().debug("dispatching update container '%s'" % container_id) | 84 | utils.get_logger().debug("dispatching update container '%s'" % container_id) |
314 | 85 | return self._find_or_create_container(container_id).update() | 85 | return self._find_or_create_container(container_id).update() |
315 | 86 | 86 | ||
316 | 87 | def list_apps(self, container_id): | ||
317 | 88 | utils.get_logger().debug("dispatching list all apps in container '%s'" % container_id) | ||
318 | 89 | return self._find_or_create_container(container_id).list_apps() | ||
319 | 90 | |||
320 | 91 | def list_app_ids(self, container_id): | 87 | def list_app_ids(self, container_id): |
321 | 92 | utils.get_logger().debug("dispatching list apps ids in container '%s'" % container_id) | 88 | utils.get_logger().debug("dispatching list apps ids in container '%s'" % container_id) |
322 | 93 | return self._find_or_create_container(container_id).list_app_ids() | 89 | return self._find_or_create_container(container_id).list_app_ids() |
323 | 94 | 90 | ||
324 | === modified file 'python/libertine/service/tasks/__init__.py' | |||
325 | --- python/libertine/service/tasks/__init__.py 2017-01-24 18:00:57 +0000 | |||
326 | +++ python/libertine/service/tasks/__init__.py 2017-02-07 13:30:52 +0000 | |||
327 | @@ -22,7 +22,6 @@ | |||
328 | 22 | from .search_task import SearchTask | 22 | from .search_task import SearchTask |
329 | 23 | from .update_task import UpdateTask | 23 | from .update_task import UpdateTask |
330 | 24 | from .list_task import ListTask | 24 | from .list_task import ListTask |
331 | 25 | from .list_apps_task import ListAppsTask | ||
332 | 26 | from .list_app_ids_task import ListAppIdsTask | 25 | from .list_app_ids_task import ListAppIdsTask |
333 | 27 | 26 | ||
334 | 28 | __all__ = [ | 27 | __all__ = [ |
335 | @@ -36,6 +35,5 @@ | |||
336 | 36 | 'SearchTask', | 35 | 'SearchTask', |
337 | 37 | 'UpdateTask', | 36 | 'UpdateTask', |
338 | 38 | 'ListTask', | 37 | 'ListTask', |
339 | 39 | 'ListAppsTask', | ||
340 | 40 | 'ListAppIdsTask' | 38 | 'ListAppIdsTask' |
341 | 41 | ] | 39 | ] |
342 | 42 | 40 | ||
343 | === removed file 'python/libertine/service/tasks/list_apps_task.py' | |||
344 | --- python/libertine/service/tasks/list_apps_task.py 2017-01-11 17:28:54 +0000 | |||
345 | +++ python/libertine/service/tasks/list_apps_task.py 1970-01-01 00:00:00 +0000 | |||
346 | @@ -1,34 +0,0 @@ | |||
347 | 1 | # Copyright 2016-2017 Canonical Ltd. | ||
348 | 2 | # | ||
349 | 3 | # This program is free software: you can redistribute it and/or modify | ||
350 | 4 | # it under the terms of the GNU General Public License as published by | ||
351 | 5 | # the Free Software Foundation; version 3 of the License. | ||
352 | 6 | # | ||
353 | 7 | # This program is distributed in the hope that it will be useful, | ||
354 | 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
355 | 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
356 | 10 | # GNU General Public License for more details. | ||
357 | 11 | # | ||
358 | 12 | # You should have received a copy of the GNU General Public License | ||
359 | 13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
360 | 14 | |||
361 | 15 | |||
362 | 16 | from .base_task import BaseTask | ||
363 | 17 | from libertine import LibertineContainer, utils | ||
364 | 18 | |||
365 | 19 | |||
366 | 20 | class ListAppsTask(BaseTask): | ||
367 | 21 | def __init__(self, container_id, config, connection, callback): | ||
368 | 22 | super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback) | ||
369 | 23 | |||
370 | 24 | def _run(self): | ||
371 | 25 | utils.get_logger().debug("Listing apps in container '%s'" % self._container) | ||
372 | 26 | container = LibertineContainer(self._container, self._config) | ||
373 | 27 | self._progress.data(str(container.list_app_launchers(use_json=True))) | ||
374 | 28 | |||
375 | 29 | def _before(self): | ||
376 | 30 | if not self._config.container_exists(self._container): | ||
377 | 31 | self._progress.error("Container '%s' does not exist, skipping list" % self._container) | ||
378 | 32 | return False | ||
379 | 33 | |||
380 | 34 | return True | ||
381 | 35 | 0 | ||
382 | === modified file 'tests/integration/test_libertine_service.py' | |||
383 | --- tests/integration/test_libertine_service.py 2017-01-24 18:00:57 +0000 | |||
384 | +++ tests/integration/test_libertine_service.py 2017-02-07 13:30:52 +0000 | |||
385 | @@ -147,8 +147,7 @@ | |||
386 | 147 | config = ContainersConfig() | 147 | config = ContainersConfig() |
387 | 148 | 148 | ||
388 | 149 | self._send(lambda: self._libertined.create('jarjar', 'JarJar Binks', 'xenial', 'mock')) | 149 | self._send(lambda: self._libertined.create('jarjar', 'JarJar Binks', 'xenial', 'mock')) |
391 | 150 | self.assertEqual({'name': 'JarJar Binks', 'app_launchers': []}, | 150 | self.assertEqual([], ast.literal_eval(self._send(lambda: self._libertined.list_app_ids('jarjar')))) |
390 | 151 | ast.literal_eval(self._send(lambda: self._libertined.list_apps('jarjar')))) | ||
392 | 152 | 151 | ||
393 | 153 | self._send(lambda: self._libertined.install('jarjar', 'the-force')) | 152 | self._send(lambda: self._libertined.install('jarjar', 'the-force')) |
394 | 154 | config.refresh_database() | 153 | config.refresh_database() |
395 | 155 | 154 | ||
396 | === modified file 'tests/unit/service/tasks/CMakeLists.txt' | |||
397 | --- tests/unit/service/tasks/CMakeLists.txt 2017-01-20 18:30:21 +0000 | |||
398 | +++ tests/unit/service/tasks/CMakeLists.txt 2017-02-07 13:30:52 +0000 | |||
399 | @@ -4,7 +4,6 @@ | |||
400 | 4 | create_service_unit_test(test_destroy_task) | 4 | create_service_unit_test(test_destroy_task) |
401 | 5 | create_service_unit_test(test_install_task) | 5 | create_service_unit_test(test_install_task) |
402 | 6 | create_service_unit_test(test_list_task) | 6 | create_service_unit_test(test_list_task) |
403 | 7 | create_service_unit_test(test_list_apps_task) | ||
404 | 8 | create_service_unit_test(test_list_app_ids_task) | 7 | create_service_unit_test(test_list_app_ids_task) |
405 | 9 | create_service_unit_test(test_remove_task) | 8 | create_service_unit_test(test_remove_task) |
406 | 10 | create_service_unit_test(test_search_task) | 9 | create_service_unit_test(test_search_task) |
407 | 11 | 10 | ||
408 | === modified file 'tests/unit/service/tasks/test_list_app_ids_task.py' | |||
409 | --- tests/unit/service/tasks/test_list_app_ids_task.py 2017-01-20 18:30:21 +0000 | |||
410 | +++ tests/unit/service/tasks/test_list_app_ids_task.py 2017-02-07 13:30:52 +0000 | |||
411 | @@ -36,7 +36,7 @@ | |||
412 | 36 | task = tasks.ListAppIdsTask('palpatine', self.config, self.connection, self.callback) | 36 | task = tasks.ListAppIdsTask('palpatine', self.config, self.connection, self.callback) |
413 | 37 | task._instant_callback = True | 37 | task._instant_callback = True |
414 | 38 | 38 | ||
416 | 39 | with unittest.mock.patch('libertine.service.tasks.list_apps_task.LibertineContainer') as MockContainer: | 39 | with unittest.mock.patch('libertine.service.tasks.list_app_ids_task.LibertineContainer') as MockContainer: |
417 | 40 | task.start().join() | 40 | task.start().join() |
418 | 41 | 41 | ||
419 | 42 | progress.error.assert_called_once_with('Container \'palpatine\' does not exist, skipping list') | 42 | progress.error.assert_called_once_with('Container \'palpatine\' does not exist, skipping list') |
420 | 43 | 43 | ||
421 | === removed file 'tests/unit/service/tasks/test_list_apps_task.py' | |||
422 | --- tests/unit/service/tasks/test_list_apps_task.py 2016-11-07 18:51:17 +0000 | |||
423 | +++ tests/unit/service/tasks/test_list_apps_task.py 1970-01-01 00:00:00 +0000 | |||
424 | @@ -1,59 +0,0 @@ | |||
425 | 1 | # Copyright 2016 Canonical Ltd. | ||
426 | 2 | # | ||
427 | 3 | # This program is free software: you can redistribute it and/or modify it | ||
428 | 4 | # under the terms of the GNU General Public License version 3, as published | ||
429 | 5 | # by the Free Software Foundation. | ||
430 | 6 | # | ||
431 | 7 | # This program is distributed in the hope that it will be useful, but | ||
432 | 8 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
433 | 9 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
434 | 10 | # PURPOSE. See the GNU General Public License for more details. | ||
435 | 11 | # | ||
436 | 12 | # You should have received a copy of the GNU General Public License along | ||
437 | 13 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
438 | 14 | |||
439 | 15 | |||
440 | 16 | import unittest.mock | ||
441 | 17 | from unittest import TestCase | ||
442 | 18 | from libertine.service import tasks | ||
443 | 19 | from libertine.ContainersConfig import ContainersConfig | ||
444 | 20 | |||
445 | 21 | |||
446 | 22 | class TestListAppsTask(TestCase): | ||
447 | 23 | def setUp(self): | ||
448 | 24 | self.config = unittest.mock.create_autospec(ContainersConfig) | ||
449 | 25 | self.connection = unittest.mock.Mock() | ||
450 | 26 | self.lock = unittest.mock.MagicMock() | ||
451 | 27 | self.called_with = None | ||
452 | 28 | |||
453 | 29 | def callback(self, task): | ||
454 | 30 | self.called_with = task | ||
455 | 31 | |||
456 | 32 | def test_sends_error_on_non_existent_container(self): | ||
457 | 33 | self.config.container_exists.return_value = False | ||
458 | 34 | with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: | ||
459 | 35 | progress = MockProgress.return_value | ||
460 | 36 | task = tasks.ListAppsTask('palpatine', self.config, self.connection, self.callback) | ||
461 | 37 | task._instant_callback = True | ||
462 | 38 | |||
463 | 39 | with unittest.mock.patch('libertine.service.tasks.list_apps_task.LibertineContainer') as MockContainer: | ||
464 | 40 | task.start().join() | ||
465 | 41 | |||
466 | 42 | progress.error.assert_called_once_with('Container \'palpatine\' does not exist, skipping list') | ||
467 | 43 | self.assertEqual(task, self.called_with) | ||
468 | 44 | |||
469 | 45 | def test_successfully_lists_apps(self): | ||
470 | 46 | self.config.container_exists.return_value = True | ||
471 | 47 | with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress: | ||
472 | 48 | progress = MockProgress.return_value | ||
473 | 49 | progress.done = False | ||
474 | 50 | task = tasks.ListAppsTask('palpatine', self.config, self.connection, self.callback) | ||
475 | 51 | task._instant_callback = True | ||
476 | 52 | |||
477 | 53 | with unittest.mock.patch('libertine.service.tasks.list_apps_task.LibertineContainer') as MockContainer: | ||
478 | 54 | MockContainer.return_value.list_app_launchers.return_value = 'jarjar\nsidius' | ||
479 | 55 | task.start().join() | ||
480 | 56 | |||
481 | 57 | progress.finished.assert_called_once_with('palpatine') | ||
482 | 58 | progress.data.assert_called_once_with('jarjar\nsidius') | ||
483 | 59 | self.assertEqual(task, self.called_with) | ||
484 | 60 | 0 | ||
485 | === modified file 'tests/unit/service/test_container.py' | |||
486 | --- tests/unit/service/test_container.py 2017-01-20 20:51:27 +0000 | |||
487 | +++ tests/unit/service/test_container.py 2017-02-07 13:30:52 +0000 | |||
488 | @@ -149,21 +149,13 @@ | |||
489 | 149 | MockUpdateTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY) | 149 | MockUpdateTask.assert_called_once_with('palpatine', self._config, unittest.mock.ANY, self._connection, unittest.mock.ANY) |
490 | 150 | MockUpdateTask.return_value.start.assert_called_once_with() | 150 | MockUpdateTask.return_value.start.assert_called_once_with() |
491 | 151 | 151 | ||
492 | 152 | def test_list_apps_creates_list_apps_task(self): | ||
493 | 153 | with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: | ||
494 | 154 | c = container.Container('palpatine', self._config, self._connection, lambda task: task) | ||
495 | 155 | with unittest.mock.patch('libertine.service.container.ListAppsTask') as MockListAppsTask: | ||
496 | 156 | c.list_apps() | ||
497 | 157 | MockListAppsTask.assert_called_once_with('palpatine', self._config, self._connection, unittest.mock.ANY) | ||
498 | 158 | MockListAppsTask.return_value.start.assert_called_once_with() | ||
499 | 159 | |||
500 | 160 | def test_list_app_ids_creates_list_app_ids_task(self): | 152 | def test_list_app_ids_creates_list_app_ids_task(self): |
501 | 161 | with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: | 153 | with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
502 | 162 | c = container.Container('palpatine', self._config, self._connection, lambda task: task) | 154 | c = container.Container('palpatine', self._config, self._connection, lambda task: task) |
504 | 163 | with unittest.mock.patch('libertine.service.container.ListAppIdsTask') as MockListAppsTask: | 155 | with unittest.mock.patch('libertine.service.container.ListAppIdsTask') as MockListAppIdsTask: |
505 | 164 | c.list_app_ids() | 156 | c.list_app_ids() |
508 | 165 | MockListAppsTask.assert_called_once_with('palpatine', self._config, self._connection, unittest.mock.ANY) | 157 | MockListAppIdsTask.assert_called_once_with('palpatine', self._config, self._connection, unittest.mock.ANY) |
509 | 166 | MockListAppsTask.return_value.start.assert_called_once_with() | 158 | MockListAppIdsTask.return_value.start.assert_called_once_with() |
510 | 167 | 159 | ||
511 | 168 | def test_removes_task_during_callback(self): | 160 | def test_removes_task_during_callback(self): |
512 | 169 | with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: | 161 | with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache: |
513 | 170 | 162 | ||
514 | === modified file 'tests/unit/service/test_task_dispatcher.py' | |||
515 | --- tests/unit/service/test_task_dispatcher.py 2017-01-20 20:51:27 +0000 | |||
516 | +++ tests/unit/service/test_task_dispatcher.py 2017-02-07 13:30:52 +0000 | |||
517 | @@ -76,14 +76,7 @@ | |||
518 | 76 | self.assertEqual(123, self._dispatcher.update('palpatine')) | 76 | self.assertEqual(123, self._dispatcher.update('palpatine')) |
519 | 77 | c.update.assert_called_once_with() | 77 | c.update.assert_called_once_with() |
520 | 78 | 78 | ||
529 | 79 | def test_list_apps_calls_list_apps_on_container(self): | 79 | def test_list_app_ids_calls_list_app_ids_on_container(self): |
522 | 80 | with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: | ||
523 | 81 | c = MockContainer.return_value | ||
524 | 82 | c.list_apps.return_value = 123 | ||
525 | 83 | self.assertEqual(123, self._dispatcher.list_apps('palpatine')) | ||
526 | 84 | c.list_apps.assert_called_once_with() | ||
527 | 85 | |||
528 | 86 | def test_list_apps_calls_list_apps_on_container(self): | ||
530 | 87 | with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: | 80 | with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
531 | 88 | c = MockContainer.return_value | 81 | c = MockContainer.return_value |
532 | 89 | c.list_app_ids.return_value = 123 | 82 | c.list_app_ids.return_value = 123 |
533 | @@ -94,20 +87,20 @@ | |||
534 | 94 | with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: | 87 | with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
535 | 95 | c = MockContainer.return_value | 88 | c = MockContainer.return_value |
536 | 96 | c.id = 'palpatine' | 89 | c.id = 'palpatine' |
540 | 97 | self._dispatcher.list_apps('palpatine') | 90 | self._dispatcher.list_app_ids('palpatine') |
541 | 98 | self._dispatcher.list_apps('palpatine') | 91 | self._dispatcher.list_app_ids('palpatine') |
542 | 99 | self._dispatcher.list_apps('palpatine') | 92 | self._dispatcher.list_app_ids('palpatine') |
543 | 100 | MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY) | 93 | MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY) |
544 | 101 | 94 | ||
545 | 102 | def test_container_callback_removes_container(self): | 95 | def test_container_callback_removes_container(self): |
546 | 103 | with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: | 96 | with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer: |
547 | 104 | c = MockContainer.return_value | 97 | c = MockContainer.return_value |
548 | 105 | c.id = 'palpatine' | 98 | c.id = 'palpatine' |
550 | 106 | self._dispatcher.list_apps('palpatine') | 99 | self._dispatcher.list_app_ids('palpatine') |
551 | 107 | MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY) | 100 | MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY) |
552 | 108 | name, args, kwargs = MockContainer.mock_calls[0] | 101 | name, args, kwargs = MockContainer.mock_calls[0] |
553 | 109 | args[len(args)-1](MockContainer.return_value) | 102 | args[len(args)-1](MockContainer.return_value) |
555 | 110 | self._dispatcher.list_apps('palpatine') | 103 | self._dispatcher.list_app_ids('palpatine') |
556 | 111 | MockContainer.assert_has_calls([ # verify container constructed twice | 104 | MockContainer.assert_has_calls([ # verify container constructed twice |
557 | 112 | unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY), | 105 | unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY), |
558 | 113 | unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY) | 106 | unittest.mock.call('palpatine', unittest.mock.ANY, self._connection, unittest.mock.ANY) |
559 | @@ -128,7 +121,7 @@ | |||
560 | 128 | c = MockContainer.return_value | 121 | c = MockContainer.return_value |
561 | 129 | c.tasks = [1, 2, 3] | 122 | c.tasks = [1, 2, 3] |
562 | 130 | c.id = 'palpatine' | 123 | c.id = 'palpatine' |
564 | 131 | self._dispatcher.list_apps('palpatine') # creates container | 124 | self._dispatcher.list_app_ids('palpatine') # creates container |
565 | 132 | self._dispatcher.container_info('palpatine') | 125 | self._dispatcher.container_info('palpatine') |
566 | 133 | MockContainerInfoTask.assert_called_once_with('palpatine', [1, 2, 3], unittest.mock.ANY, self._connection, unittest.mock.ANY) | 126 | MockContainerInfoTask.assert_called_once_with('palpatine', [1, 2, 3], unittest.mock.ANY, self._connection, unittest.mock.ANY) |
567 | 134 | task.start.assert_called_once_with() | 127 | task.start.assert_called_once_with() |
568 | 135 | 128 | ||
569 | === removed file 'tests/unit/test_app_discovery.py' | |||
570 | --- tests/unit/test_app_discovery.py 2015-12-22 17:28:10 +0000 | |||
571 | +++ tests/unit/test_app_discovery.py 1970-01-01 00:00:00 +0000 | |||
572 | @@ -1,52 +0,0 @@ | |||
573 | 1 | """Unit tests for the AppLauncher module.""" | ||
574 | 2 | # Copyright 2015 Canonical Ltd. | ||
575 | 3 | # | ||
576 | 4 | # This program is free software: you can redistribute it and/or modify it | ||
577 | 5 | # under the terms of the GNU General Public License version 3, as published | ||
578 | 6 | # by the Free Software Foundation. | ||
579 | 7 | # | ||
580 | 8 | # This program is distributed in the hope that it will be useful, but | ||
581 | 9 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
582 | 10 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
583 | 11 | # PURPOSE. See the GNU General Public License for more details. | ||
584 | 12 | # | ||
585 | 13 | # You should have received a copy of the GNU General Public License along | ||
586 | 14 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
587 | 15 | |||
588 | 16 | from libertine.AppDiscovery import IconCache | ||
589 | 17 | from testtools import TestCase | ||
590 | 18 | from testtools.matchers import Equals | ||
591 | 19 | |||
592 | 20 | |||
593 | 21 | class TestIconSearching(TestCase): | ||
594 | 22 | |||
595 | 23 | def mock_file_loader(self, root_path): | ||
596 | 24 | return [ | ||
597 | 25 | "/some/path/128x128/icon.png", | ||
598 | 26 | "/somepath/icon.png", | ||
599 | 27 | "/somepath/testme.png" | ||
600 | 28 | ]; | ||
601 | 29 | |||
602 | 30 | def test_full_path(self): | ||
603 | 31 | some_root = "/some_root" | ||
604 | 32 | icon_cache = IconCache(some_root, file_loader=self.mock_file_loader) | ||
605 | 33 | |||
606 | 34 | some_full_path = "/this/is/a/full/path" | ||
607 | 35 | self.assertThat(icon_cache.expand_icons(some_full_path)[0], | ||
608 | 36 | Equals(some_full_path)) | ||
609 | 37 | |||
610 | 38 | def test_icon_name(self): | ||
611 | 39 | some_root = "/some_root" | ||
612 | 40 | icon_cache = IconCache(some_root, file_loader=self.mock_file_loader) | ||
613 | 41 | |||
614 | 42 | some_icon_list = "testme" | ||
615 | 43 | self.assertThat(icon_cache.expand_icons(some_icon_list)[0], | ||
616 | 44 | Equals("/somepath/testme.png")) | ||
617 | 45 | |||
618 | 46 | def test_edge_case_relative_file_name(self): | ||
619 | 47 | some_root = "/some_root" | ||
620 | 48 | icon_cache = IconCache(some_root, file_loader=self.mock_file_loader) | ||
621 | 49 | |||
622 | 50 | some_icon_list = "testme.png" | ||
623 | 51 | self.assertThat(icon_cache.expand_icons(some_icon_list)[0], | ||
624 | 52 | Equals("/somepath/testme.png")) | ||
625 | 53 | 0 | ||
626 | === modified file 'tools/libertine-container-manager' | |||
627 | --- tools/libertine-container-manager 2017-02-01 21:13:14 +0000 | |||
628 | +++ tools/libertine-container-manager 2017-02-07 13:30:52 +0000 | |||
629 | @@ -17,8 +17,9 @@ | |||
630 | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
631 | 18 | 18 | ||
632 | 19 | import argparse | 19 | import argparse |
633 | 20 | import getpass | ||
634 | 21 | import json | ||
635 | 20 | import libertine.utils | 22 | import libertine.utils |
636 | 21 | import getpass | ||
637 | 22 | import os | 23 | import os |
638 | 23 | import sys | 24 | import sys |
639 | 24 | import re | 25 | import re |
640 | @@ -224,8 +225,12 @@ | |||
641 | 224 | def list_apps(self, args): | 225 | def list_apps(self, args): |
642 | 225 | container_id = self.containers_config.check_container_id(args.id) | 226 | container_id = self.containers_config.check_container_id(args.id) |
643 | 226 | 227 | ||
646 | 227 | container = LibertineContainer(container_id) | 228 | app_ids = LibertineContainer(container_id).list_app_ids() |
647 | 228 | print(container.list_app_launchers(use_json=args.json)) | 229 | if args.json: |
648 | 230 | print(json.dumps(app_ids)) | ||
649 | 231 | else: | ||
650 | 232 | for app in app_ids: | ||
651 | 233 | print(app) | ||
652 | 229 | 234 | ||
653 | 230 | def exec(self, args): | 235 | def exec(self, args): |
654 | 231 | container_id = self.containers_config.check_container_id(args.id) | 236 | container_id = self.containers_config.check_container_id(args.id) |
FAILED: Continuous integration, rev:391 /jenkins. canonical. com/libertine/ job/lp- libertine- ci/368/ /jenkins. canonical. com/libertine/ job/build/ 707/console /jenkins. canonical. com/libertine/ job/build- 0-fetch/ 717 /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= xenial+ overlay/ 698/console /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= zesty/698/ console /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= xenial+ overlay/ 698/console /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= zesty/698/ console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /jenkins. canonical. com/libertine/ job/lp- libertine- ci/368/ rebuild
https:/