Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | 444 |
Merged at revision: | 426 |
Proposed branch: | lp:click/devel |
Merge into: | lp:click |
Diff against target: |
1791 lines (+740/-253) 14 files modified
click/chroot.py (+82/-25) click/commands/__init__.py (+1/-0) click/commands/chroot.py (+69/-9) click/commands/framework.py (+42/-0) click/tests/helpers.py (+22/-0) click/tests/test_build.py (+10/-1) click/tests/test_chroot.py (+123/-0) click/tests/test_hooks.py (+261/-196) click/tests/test_install.py (+9/-9) debian/changelog (+38/-0) debian/control (+1/-1) doc/index.rst (+4/-2) doc/manpage.rst (+6/-0) lib/click/hooks.vala (+72/-10) |
To merge this branch: | bzr merge lp:click/devel |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson | Approve | ||
Review via email: mp+220239@code.launchpad.net |
Commit message
Click 0.4.23: handle framework removal, x86 chroot support, and various other small fixes.
Description of the change
[ Michael Vogt ]
* Show human-readable error message when a click chroot subcommand fails
because of existing or non-existing chroots (LP: #1296820).
* Selectively disable logging on some tests to avoid message spam during
the test runs.
* When running hooks, remove hook symlinks if framework requirements are
not met (LP: #1271944).
* Cleanup the chroot if "click chroot create" fails (unless
--keep-
* Fix sources.list generation when native_arch and target_arch are on the
same archive server (part of LP #1319153).
* Add "click framework list" command to list available frameworks
(LP: #1294659).
[ Pete Woods ]
* Add libunity-scopes-dev package to chroot (LP: #1320786).
[ Sergio Schvezov ]
* click chroot creation depends on dpkg-architecture, so recommend
dpkg-dev.
[ Colin Watson ]
* chroot: Handle the case where we can execute binaries for the target
architecture directly and thus don't need a cross-compiler
(LP: #1319153).
Colin Watson (cjwatson) : | # |
- 445. By Colin Watson
-
releasing package click version 0.4.23
- 446. By Colin Watson
-
chroot: Force dpkg-architecture to recalculate everything rather than
picking up values from the environment, to avoid the test suite getting
confused by environment variables exported by dpkg-buildpackage.
Preview Diff
1 | === modified file 'click/chroot.py' |
2 | --- click/chroot.py 2014-03-31 11:11:26 +0000 |
3 | +++ click/chroot.py 2014-05-20 13:13:48 +0000 |
4 | @@ -22,6 +22,8 @@ |
5 | __all__ = [ |
6 | "ClickChroot", |
7 | "ClickChrootException", |
8 | + "ClickChrootAlreadyExistsException", |
9 | + "ClickChrootDoesNotExistException", |
10 | ] |
11 | |
12 | |
13 | @@ -72,6 +74,7 @@ |
14 | "libqt5svg5-dev:TARGET", |
15 | "libqt5webkit5-dev:TARGET", |
16 | "libqt5xmlpatterns5-dev:TARGET", |
17 | + "libunity-scopes-dev:TARGET", |
18 | "qt3d5-dev:TARGET", |
19 | "qt5-default:TARGET", |
20 | "qtbase5-dev:TARGET", |
21 | @@ -104,6 +107,17 @@ |
22 | |
23 | |
24 | class ClickChrootException(Exception): |
25 | + """A generic issue with the chroot""" |
26 | + pass |
27 | + |
28 | + |
29 | +class ClickChrootAlreadyExistsException(ClickChrootException): |
30 | + """The chroot already exists""" |
31 | + pass |
32 | + |
33 | + |
34 | +class ClickChrootDoesNotExistException(ClickChrootException): |
35 | + """A chroot with that name does not exist yet""" |
36 | pass |
37 | |
38 | |
39 | @@ -118,9 +132,10 @@ |
40 | series = framework_series[self.framework_base] |
41 | self.series = series |
42 | self.session = session |
43 | - self.native_arch = subprocess.check_output( |
44 | + system_arch = subprocess.check_output( |
45 | ["dpkg", "--print-architecture"], |
46 | universal_newlines=True).strip() |
47 | + self.native_arch = self._get_native_arch(system_arch, self.target_arch) |
48 | self.chroots_dir = "/var/lib/schroot/chroots" |
49 | # this doesn't work because we are running this under sudo |
50 | if 'DEBOOTSTRAP_MIRROR' in os.environ: |
51 | @@ -135,11 +150,35 @@ |
52 | self.user = pwd.getpwuid(os.getuid()).pw_name |
53 | self.dpkg_architecture = self._dpkg_architecture() |
54 | |
55 | + def _get_native_arch(self, system_arch, target_arch): |
56 | + """Determine the proper native architecture for a chroot. |
57 | + |
58 | + Some combinations of system and target architecture do not require |
59 | + cross-building, so in these cases we just create a chroot suitable |
60 | + for native building. |
61 | + """ |
62 | + if (system_arch, target_arch) in ( |
63 | + ("amd64", "i386"), |
64 | + # This will only work if the system is running a 64-bit |
65 | + # kernel; but there's no alternative since no i386-to-amd64 |
66 | + # cross-compiler is available in the Ubuntu archive. |
67 | + ("i386", "amd64"), |
68 | + ): |
69 | + return target_arch |
70 | + else: |
71 | + return system_arch |
72 | + |
73 | def _dpkg_architecture(self): |
74 | dpkg_architecture = {} |
75 | command = ["dpkg-architecture", "-a%s" % self.target_arch] |
76 | env = dict(os.environ) |
77 | env["CC"] = "true" |
78 | + # Force dpkg-architecture to recalculate everything rather than |
79 | + # picking up values from the environment, which will be present when |
80 | + # running the test suite under dpkg-buildpackage. |
81 | + for key in list(env): |
82 | + if key.startswith("DEB_BUILD_") or key.startswith("DEB_HOST_"): |
83 | + del env[key] |
84 | lines = subprocess.check_output( |
85 | command, env=env, universal_newlines=True).splitlines() |
86 | for line in lines: |
87 | @@ -156,18 +195,23 @@ |
88 | for pocket in ['updates', 'security']: |
89 | pockets.append('%s-%s' % (series, pocket)) |
90 | sources = [] |
91 | - if target_arch not in primary_arches: |
92 | - for pocket in pockets: |
93 | - sources.append("deb [arch=%s] %s %s %s" % |
94 | - (target_arch, ports_mirror, pocket, components)) |
95 | - sources.append("deb-src %s %s %s" % |
96 | - (ports_mirror, pocket, components)) |
97 | - if native_arch in primary_arches: |
98 | - for pocket in pockets: |
99 | - sources.append("deb [arch=%s] %s %s %s" % |
100 | - (native_arch, self.archive, pocket, components)) |
101 | - sources.append("deb-src %s %s %s" % |
102 | - (self.archive, pocket, components)) |
103 | + # write binary lines |
104 | + arches = [target_arch] |
105 | + if native_arch != target_arch: |
106 | + arches.append(native_arch) |
107 | + for arch in arches: |
108 | + if arch not in primary_arches: |
109 | + mirror = ports_mirror |
110 | + else: |
111 | + mirror = self.archive |
112 | + for pocket in pockets: |
113 | + sources.append("deb [arch=%s] %s %s %s" % |
114 | + (arch, mirror, pocket, components)) |
115 | + # write source lines |
116 | + for pocket in pockets: |
117 | + sources.append("deb-src %s %s %s" % |
118 | + (self.archive, pocket, components)) |
119 | + |
120 | return sources |
121 | |
122 | @property |
123 | @@ -195,9 +239,16 @@ |
124 | mode = stat.S_IMODE(os.stat(path).st_mode) |
125 | os.chmod(path, mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) |
126 | |
127 | - def create(self): |
128 | + def _make_cross_package(self, prefix): |
129 | + if self.native_arch == self.target_arch: |
130 | + return prefix |
131 | + else: |
132 | + target_tuple = self.dpkg_architecture["DEB_HOST_GNU_TYPE"] |
133 | + return "%s-%s" % (prefix, target_tuple) |
134 | + |
135 | + def create(self, keep_broken_chroot_on_fail=False): |
136 | if self.exists(): |
137 | - raise ClickChrootException( |
138 | + raise ClickChrootAlreadyExistsException( |
139 | "Chroot %s already exists" % self.full_name) |
140 | components = ["main", "restricted", "universe", "multiverse"] |
141 | mount = "%s/%s" % (self.chroots_dir, self.full_name) |
142 | @@ -208,11 +259,10 @@ |
143 | proxy = subprocess.check_output( |
144 | 'unset x; eval "$(apt-config shell x Acquire::HTTP::Proxy)"; echo "$x"', |
145 | shell=True, universal_newlines=True).strip() |
146 | - target_tuple = self.dpkg_architecture["DEB_HOST_GNU_TYPE"] |
147 | build_pkgs = [ |
148 | "build-essential", "fakeroot", |
149 | - "apt-utils", "g++-%s" % target_tuple, |
150 | - "pkg-config-%s" % target_tuple, "cmake", |
151 | + "apt-utils", self._make_cross_package("g++"), |
152 | + self._make_cross_package("pkg-config"), "cmake", |
153 | "dpkg-cross", "libc-dev:%s" % self.target_arch |
154 | ] |
155 | for package in extra_packages.get(self.framework_base, []): |
156 | @@ -312,11 +362,18 @@ |
157 | print("apt-get clean", file=finish) |
158 | self._make_executable(finish_script) |
159 | command = ["/finish.sh"] |
160 | - return self.maint(*command) |
161 | + ret_code = self.maint(*command) |
162 | + if ret_code != 0 and not keep_broken_chroot_on_fail: |
163 | + # cleanup on failure |
164 | + self.destroy() |
165 | + raise ClickChrootException( |
166 | + "Failed to create chroot '{}' (exit status {})".format( |
167 | + self.full_name, ret_code)) |
168 | + return ret_code |
169 | |
170 | def run(self, *args): |
171 | if not self.exists(): |
172 | - raise ClickChrootException( |
173 | + raise ClickChrootDoesNotExistException( |
174 | "Chroot %s does not exist" % self.full_name) |
175 | command = ["schroot", "-c"] |
176 | if self.session: |
177 | @@ -353,7 +410,7 @@ |
178 | |
179 | def install(self, *pkgs): |
180 | if not self.exists(): |
181 | - raise ClickChrootException( |
182 | + raise ClickChrootDoesNotExistException( |
183 | "Chroot %s does not exist" % self.full_name) |
184 | ret = self.update() |
185 | if ret != 0: |
186 | @@ -375,7 +432,7 @@ |
187 | |
188 | def upgrade(self): |
189 | if not self.exists(): |
190 | - raise ClickChrootException( |
191 | + raise ClickChrootDoesNotExistException( |
192 | "Chroot %s does not exist" % self.full_name) |
193 | ret = self.update() |
194 | if ret != 0: |
195 | @@ -388,7 +445,7 @@ |
196 | |
197 | def destroy(self): |
198 | if not self.exists(): |
199 | - raise ClickChrootException( |
200 | + raise ClickChrootDoesNotExistException( |
201 | "Chroot %s does not exist" % self.full_name) |
202 | chroot_config = "/etc/schroot/chroot.d/%s" % self.full_name |
203 | os.remove(chroot_config) |
204 | @@ -398,7 +455,7 @@ |
205 | |
206 | def begin_session(self): |
207 | if not self.exists(): |
208 | - raise ClickChrootException( |
209 | + raise ClickChrootDoesNotExistException( |
210 | "Chroot %s does not exist" % self.full_name) |
211 | command = ["schroot", "-c", self.full_name, "--begin-session", |
212 | "--session-name", self.full_session_name] |
213 | @@ -407,7 +464,7 @@ |
214 | |
215 | def end_session(self): |
216 | if not self.exists(): |
217 | - raise ClickChrootException( |
218 | + raise ClickChrootDoesNotExistException( |
219 | "Chroot %s does not exist" % self.full_name) |
220 | command = ["schroot", "-c", self.full_session_name, "--end-session"] |
221 | subprocess.check_call(command) |
222 | |
223 | === modified file 'click/commands/__init__.py' |
224 | --- click/commands/__init__.py 2013-10-31 20:17:19 +0000 |
225 | +++ click/commands/__init__.py 2014-05-20 13:13:48 +0000 |
226 | @@ -24,6 +24,7 @@ |
227 | "chroot", |
228 | "contents", |
229 | "desktophook", |
230 | + "framework", |
231 | "hook", |
232 | "info", |
233 | "install", |
234 | |
235 | === modified file 'click/commands/chroot.py' |
236 | --- click/commands/chroot.py 2014-03-31 11:11:26 +0000 |
237 | +++ click/commands/chroot.py 2014-05-20 13:13:48 +0000 |
238 | @@ -20,9 +20,14 @@ |
239 | from __future__ import print_function |
240 | |
241 | from argparse import ArgumentParser, REMAINDER |
242 | +from contextlib import contextmanager |
243 | import os |
244 | |
245 | -from click.chroot import ClickChroot |
246 | +from click.chroot import ( |
247 | + ClickChroot, |
248 | + ClickChrootAlreadyExistsException, |
249 | + ClickChrootDoesNotExistException, |
250 | +) |
251 | from click import osextras |
252 | |
253 | |
254 | @@ -31,6 +36,25 @@ |
255 | parser.error("must be run as root; try sudo") |
256 | |
257 | |
258 | +@contextmanager |
259 | +def message_on_error(exc, msg): |
260 | + """ |
261 | + Context Manager that prints the error message 'msg' on exception 'exc' |
262 | + """ |
263 | + try: |
264 | + yield |
265 | + except exc: |
266 | + print(msg) |
267 | + |
268 | + |
269 | +# FIXME: i18n(?) |
270 | +class ErrorMessages: |
271 | + EXISTS = """A chroot for that name and architecture already exists. |
272 | +Please see the man-page how to use it.""" |
273 | + NOT_EXISTS = """A chroot for that name and architecture does not exist. |
274 | +Please use 'create' to create it.""" |
275 | + |
276 | + |
277 | def create(parser, args): |
278 | if not osextras.find_on_path("debootstrap"): |
279 | parser.error( |
280 | @@ -38,20 +62,32 @@ |
281 | "debootstrap") |
282 | requires_root(parser) |
283 | chroot = ClickChroot(args.architecture, args.framework, series=args.series) |
284 | - return chroot.create() |
285 | + with message_on_error( |
286 | + ClickChrootAlreadyExistsException, ErrorMessages.EXISTS): |
287 | + return chroot.create(args.keep_broken_chroot) |
288 | + # if we reach this point there was a error so return exit_status 1 |
289 | + return 1 |
290 | |
291 | |
292 | def install(parser, args): |
293 | packages = args.packages |
294 | chroot = ClickChroot(args.architecture, args.framework) |
295 | - return chroot.install(*packages) |
296 | + with message_on_error( |
297 | + ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS): |
298 | + return chroot.install(*packages) |
299 | + # if we reach this point there was a error so return exit_status 1 |
300 | + return 1 |
301 | |
302 | |
303 | def destroy(parser, args): |
304 | requires_root(parser) |
305 | # ask for confirmation? |
306 | chroot = ClickChroot(args.architecture, args.framework) |
307 | - return chroot.destroy() |
308 | + with message_on_error( |
309 | + ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS): |
310 | + return chroot.destroy() |
311 | + # if we reach this point there was a error so return exit_status 1 |
312 | + return 1 |
313 | |
314 | |
315 | def execute(parser, args): |
316 | @@ -60,7 +96,11 @@ |
317 | program = ["/bin/bash"] |
318 | chroot = ClickChroot( |
319 | args.architecture, args.framework, session=args.session) |
320 | - return chroot.run(*program) |
321 | + with message_on_error( |
322 | + ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS): |
323 | + return chroot.run(*program) |
324 | + # if we reach this point there was a error so return exit_status 1 |
325 | + return 1 |
326 | |
327 | |
328 | def maint(parser, args): |
329 | @@ -69,24 +109,40 @@ |
330 | program = ["/bin/bash"] |
331 | chroot = ClickChroot( |
332 | args.architecture, args.framework, session=args.session) |
333 | - return chroot.maint(*program) |
334 | + with message_on_error( |
335 | + ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS): |
336 | + return chroot.maint(*program) |
337 | + # if we reach this point there was a error so return exit_status 1 |
338 | + return 1 |
339 | |
340 | |
341 | def upgrade(parser, args): |
342 | chroot = ClickChroot(args.architecture, args.framework) |
343 | - return chroot.upgrade() |
344 | + with message_on_error( |
345 | + ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS): |
346 | + return chroot.upgrade() |
347 | + # if we reach this point there was a error so return exit_status 1 |
348 | + return 1 |
349 | |
350 | |
351 | def begin_session(parser, args): |
352 | chroot = ClickChroot( |
353 | args.architecture, args.framework, session=args.session) |
354 | - return chroot.begin_session() |
355 | + with message_on_error( |
356 | + ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS): |
357 | + return chroot.begin_session() |
358 | + # if we reach this point there was a error so return exit_status 1 |
359 | + return 1 |
360 | |
361 | |
362 | def end_session(parser, args): |
363 | chroot = ClickChroot( |
364 | args.architecture, args.framework, session=args.session) |
365 | - return chroot.end_session() |
366 | + with message_on_error( |
367 | + ClickChrootDoesNotExistException, ErrorMessages.NOT_EXISTS): |
368 | + return chroot.end_session() |
369 | + # if we reach this point there was a error so return exit_status 1 |
370 | + return 1 |
371 | |
372 | |
373 | def run(argv): |
374 | @@ -107,6 +163,10 @@ |
375 | create_parser = subparsers.add_parser( |
376 | "create", |
377 | help="create a chroot of the provided architecture") |
378 | + create_parser.add_argument( |
379 | + "-k", "--keep-broken-chroot", default=False, action="store_true", |
380 | + help="Keep the chroot even if creating it fails (default is to delete " |
381 | + "it)") |
382 | create_parser.set_defaults(func=create) |
383 | destroy_parser = subparsers.add_parser( |
384 | "destroy", |
385 | |
386 | === added file 'click/commands/framework.py' |
387 | --- click/commands/framework.py 1970-01-01 00:00:00 +0000 |
388 | +++ click/commands/framework.py 2014-05-20 13:13:48 +0000 |
389 | @@ -0,0 +1,42 @@ |
390 | +# Copyright (C) 2014 Canonical Ltd. |
391 | +# Author: Michael Vogt <mvo@ubuntu.com> |
392 | + |
393 | +# This program is free software: you can redistribute it and/or modify |
394 | +# it under the terms of the GNU General Public License as published by |
395 | +# the Free Software Foundation; version 3 of the License. |
396 | +# |
397 | +# This program is distributed in the hope that it will be useful, |
398 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
399 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
400 | +# GNU General Public License for more details. |
401 | +# |
402 | +# You should have received a copy of the GNU General Public License |
403 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
404 | + |
405 | +"""List available frameworks.""" |
406 | + |
407 | +from __future__ import print_function |
408 | + |
409 | +from argparse import ArgumentParser |
410 | + |
411 | +from gi.repository import Click |
412 | + |
413 | + |
414 | +def list(parser, args): |
415 | + for framework in Click.Framework.get_frameworks(): |
416 | + print("%s" % framework.props.name) |
417 | + return 0 |
418 | + |
419 | + |
420 | +def run(argv): |
421 | + parser = ArgumentParser("click framework") |
422 | + subparsers = parser.add_subparsers() |
423 | + list_parser = subparsers.add_parser( |
424 | + "list", |
425 | + help="list available frameworks") |
426 | + list_parser.set_defaults(func=list) |
427 | + args = parser.parse_args(argv) |
428 | + if not hasattr(args, "func"): |
429 | + parser.print_help() |
430 | + return 1 |
431 | + return args.func(parser, args) |
432 | |
433 | === modified file 'click/tests/helpers.py' |
434 | --- click/tests/helpers.py 2014-05-08 18:18:39 +0000 |
435 | +++ click/tests/helpers.py 2014-05-20 13:13:48 +0000 |
436 | @@ -29,6 +29,7 @@ |
437 | |
438 | |
439 | import contextlib |
440 | +from functools import wraps |
441 | import os |
442 | import re |
443 | import shutil |
444 | @@ -45,6 +46,19 @@ |
445 | from click.tests import gimock |
446 | |
447 | |
448 | +def disable_logging(func): |
449 | + """Decorator to disable logging e.g. during a test""" |
450 | + @wraps(func) |
451 | + def wrapper(*args, **kwargs): |
452 | + import logging |
453 | + logging.disable(logging.CRITICAL) |
454 | + try: |
455 | + return func(*args, **kwargs) |
456 | + finally: |
457 | + logging.disable(logging.NOTSET) |
458 | + return wrapper |
459 | + |
460 | + |
461 | class TestCase(gimock.GIMockTestCase): |
462 | def setUp(self): |
463 | super(TestCase, self).setUp() |
464 | @@ -108,6 +122,14 @@ |
465 | self.assertRaisesGError( |
466 | "click_user_error-quark", code, callableObj, *args, **kwargs) |
467 | |
468 | + def _setup_frameworks(self, preloads, frameworks_dir=None, frameworks=[]): |
469 | + frameworks_dir = self._create_mock_framework_dir(frameworks_dir) |
470 | + shutil.rmtree(frameworks_dir, ignore_errors=True) |
471 | + for framework in frameworks: |
472 | + self._create_mock_framework_file(framework) |
473 | + preloads["click_get_frameworks_dir"].side_effect = ( |
474 | + lambda: self.make_string(frameworks_dir)) |
475 | + |
476 | def _create_mock_framework_dir(self, frameworks_dir=None): |
477 | if frameworks_dir is None: |
478 | frameworks_dir = os.path.join(self.temp_dir, "frameworks") |
479 | |
480 | === modified file 'click/tests/test_build.py' |
481 | --- click/tests/test_build.py 2014-05-05 13:10:19 +0000 |
482 | +++ click/tests/test_build.py 2014-05-20 13:13:48 +0000 |
483 | @@ -33,7 +33,12 @@ |
484 | |
485 | from click.build import ClickBuildError, ClickBuilder, ClickSourceBuilder |
486 | from click.preinst import static_preinst |
487 | -from click.tests.helpers import TestCase, mkfile, touch |
488 | +from click.tests.helpers import ( |
489 | + disable_logging, |
490 | + mkfile, |
491 | + TestCase, |
492 | + touch, |
493 | +) |
494 | |
495 | |
496 | # BAW 2013-04-15: Some tests require umask 022. Use this decorator to |
497 | @@ -118,6 +123,7 @@ |
498 | ["dpkg-deb", "-f", path, name], |
499 | universal_newlines=True).rstrip("\n") |
500 | |
501 | + @disable_logging |
502 | @umask(0o22) |
503 | def test_build(self): |
504 | self.use_temp_dir() |
505 | @@ -199,6 +205,7 @@ |
506 | self.assertEqual( |
507 | "foo", os.readlink(os.path.join(extract_path, "bin", "bar"))) |
508 | |
509 | + @disable_logging |
510 | def test_build_excludes_dot_click(self): |
511 | self.use_temp_dir() |
512 | scratch = os.path.join(self.temp_dir, "scratch") |
513 | @@ -218,6 +225,7 @@ |
514 | subprocess.check_call(["dpkg-deb", "-x", path, extract_path]) |
515 | self.assertEqual([], os.listdir(extract_path)) |
516 | |
517 | + @disable_logging |
518 | def test_build_multiple_architectures(self): |
519 | self.use_temp_dir() |
520 | scratch = os.path.join(self.temp_dir, "scratch") |
521 | @@ -246,6 +254,7 @@ |
522 | self.assertEqual(source_json, target_json) |
523 | |
524 | # FIXME: DRY violation with test_build_multiple_architectures etc |
525 | + @disable_logging |
526 | def test_build_multiple_frameworks(self): |
527 | self.use_temp_dir() |
528 | scratch = os.path.join(self.temp_dir, "scratch") |
529 | |
530 | === added file 'click/tests/test_chroot.py' |
531 | --- click/tests/test_chroot.py 1970-01-01 00:00:00 +0000 |
532 | +++ click/tests/test_chroot.py 2014-05-20 13:13:48 +0000 |
533 | @@ -0,0 +1,123 @@ |
534 | +# Copyright (C) 2014 Canonical Ltd. |
535 | +# Author: Michael Vogt |
536 | + |
537 | +# This program is free software: you can redistribute it and/or modify |
538 | +# it under the terms of the GNU General Public License as published by |
539 | +# the Free Software Foundation; version 3 of the License. |
540 | +# |
541 | +# This program is distributed in the hope that it will be useful, |
542 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
543 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
544 | +# GNU General Public License for more details. |
545 | +# |
546 | +# You should have received a copy of the GNU General Public License |
547 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
548 | + |
549 | +"""Unit tests for click.chroot.""" |
550 | + |
551 | +from __future__ import print_function |
552 | + |
553 | +__metaclass__ = type |
554 | +__all__ = [ |
555 | + 'TestClickChroot', |
556 | + ] |
557 | + |
558 | + |
559 | +from click.tests.helpers import TestCase |
560 | +from click.chroot import ( |
561 | + ClickChroot, |
562 | +) |
563 | + |
564 | + |
565 | +class TestClickChroot(TestCase): |
566 | + def test_get_native_arch_amd64_to_amd64(self): |
567 | + chroot = ClickChroot("amd64", "ubuntu-sdk-14.04", series="trusty") |
568 | + self.assertEqual("amd64", chroot._get_native_arch("amd64", "amd64")) |
569 | + |
570 | + def test_get_native_arch_amd64_to_armhf(self): |
571 | + chroot = ClickChroot("armhf", "ubuntu-sdk-14.04", series="trusty") |
572 | + self.assertEqual("amd64", chroot._get_native_arch("amd64", "armhf")) |
573 | + |
574 | + def test_get_native_arch_amd64_to_i386(self): |
575 | + chroot = ClickChroot("i386", "ubuntu-sdk-14.04", series="trusty") |
576 | + self.assertEqual("i386", chroot._get_native_arch("amd64", "i386")) |
577 | + |
578 | + def test_gen_sources_archive_only(self): |
579 | + chroot = ClickChroot("amd64", "ubuntu-sdk-13.10", series="trusty") |
580 | + chroot.native_arch = "i386" |
581 | + sources = chroot._generate_sources( |
582 | + chroot.series, chroot.native_arch, chroot.target_arch, |
583 | + "main") |
584 | + self.assertEqual([ |
585 | + 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu trusty main', |
586 | + 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu trusty-updates main', |
587 | + 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu trusty-security main', |
588 | + 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty main', |
589 | + 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-updates main', |
590 | + 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-security main', |
591 | + 'deb-src http://archive.ubuntu.com/ubuntu trusty main', |
592 | + 'deb-src http://archive.ubuntu.com/ubuntu trusty-updates main', |
593 | + 'deb-src http://archive.ubuntu.com/ubuntu trusty-security main', |
594 | + ], sources) |
595 | + |
596 | + def test_gen_sources_mixed_archive_ports(self): |
597 | + chroot = ClickChroot("armhf", "ubuntu-sdk-13.10", series="trusty") |
598 | + chroot.native_arch = "i386" |
599 | + sources = chroot._generate_sources( |
600 | + chroot.series, chroot.native_arch, chroot.target_arch, |
601 | + "main") |
602 | + self.assertEqual([ |
603 | + 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty main', |
604 | + 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty-updates main', |
605 | + 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty-security main', |
606 | + 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty main', |
607 | + 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-updates main', |
608 | + 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-security main', |
609 | + 'deb-src http://archive.ubuntu.com/ubuntu trusty main', |
610 | + 'deb-src http://archive.ubuntu.com/ubuntu trusty-updates main', |
611 | + 'deb-src http://archive.ubuntu.com/ubuntu trusty-security main', |
612 | + ], sources) |
613 | + |
614 | + def test_gen_sources_ports_only(self): |
615 | + chroot = ClickChroot("armhf", "ubuntu-sdk-13.10", series="trusty") |
616 | + chroot.native_arch = "armel" |
617 | + sources = chroot._generate_sources( |
618 | + chroot.series, chroot.native_arch, chroot.target_arch, |
619 | + "main") |
620 | + self.assertEqual([ |
621 | + 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty main', |
622 | + 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty-updates main', |
623 | + 'deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports trusty-security main', |
624 | + 'deb [arch=armel] http://ports.ubuntu.com/ubuntu-ports trusty main', |
625 | + 'deb [arch=armel] http://ports.ubuntu.com/ubuntu-ports trusty-updates main', |
626 | + 'deb [arch=armel] http://ports.ubuntu.com/ubuntu-ports trusty-security main', |
627 | + 'deb-src http://archive.ubuntu.com/ubuntu trusty main', |
628 | + 'deb-src http://archive.ubuntu.com/ubuntu trusty-updates main', |
629 | + 'deb-src http://archive.ubuntu.com/ubuntu trusty-security main', |
630 | + ], sources) |
631 | + |
632 | + def test_gen_sources_native(self): |
633 | + chroot = ClickChroot("i386", "ubuntu-sdk-14.04", series="trusty") |
634 | + chroot.native_arch = "i386" |
635 | + sources = chroot._generate_sources( |
636 | + chroot.series, chroot.native_arch, chroot.target_arch, |
637 | + "main") |
638 | + self.assertEqual([ |
639 | + 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty main', |
640 | + 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-updates main', |
641 | + 'deb [arch=i386] http://archive.ubuntu.com/ubuntu trusty-security main', |
642 | + 'deb-src http://archive.ubuntu.com/ubuntu trusty main', |
643 | + 'deb-src http://archive.ubuntu.com/ubuntu trusty-updates main', |
644 | + 'deb-src http://archive.ubuntu.com/ubuntu trusty-security main', |
645 | + ], sources) |
646 | + |
647 | + def test_make_cross_package_native(self): |
648 | + chroot = ClickChroot("amd64", "ubuntu-sdk-14.04", series="trusty") |
649 | + chroot.native_arch = "amd64" |
650 | + self.assertEqual("g++", chroot._make_cross_package("g++")) |
651 | + |
652 | + def test_make_cross_package_cross(self): |
653 | + chroot = ClickChroot("armhf", "ubuntu-sdk-14.04", series="trusty") |
654 | + chroot.native_arch = "amd64" |
655 | + self.assertEqual( |
656 | + "g++-arm-linux-gnueabihf", chroot._make_cross_package("g++")) |
657 | |
658 | === modified file 'click/tests/test_hooks.py' |
659 | --- click/tests/test_hooks.py 2014-04-03 08:52:02 +0000 |
660 | +++ click/tests/test_hooks.py 2014-05-20 13:13:48 +0000 |
661 | @@ -95,6 +95,9 @@ |
662 | |
663 | |
664 | class TestClickHookBase(TestCase): |
665 | + |
666 | + TEST_USER = "test-user" |
667 | + |
668 | def setUp(self): |
669 | super(TestClickHookBase, self).setUp() |
670 | self.use_temp_dir() |
671 | @@ -102,11 +105,33 @@ |
672 | self.db.add(self.temp_dir) |
673 | self.spawn_calls = [] |
674 | |
675 | + def _make_installed_click(self, package="test-1",version="1.0", |
676 | + json_data={}, |
677 | + make_current=True, |
678 | + all_users=False): |
679 | + with mkfile_utf8(os.path.join( |
680 | + self.temp_dir, package, version, ".click", "info", |
681 | + "%s.manifest" % package)) as f: |
682 | + json.dump(json_data, f, ensure_ascii=False) |
683 | + if make_current: |
684 | + os.symlink(version, os.path.join(self.temp_dir, package, "current")) |
685 | + if all_users: |
686 | + db = Click.User.for_all_users(self.db) |
687 | + else: |
688 | + db = Click.User.for_user(self.db, self.TEST_USER) |
689 | + db.set_version(package, version) |
690 | + |
691 | + def _make_hook_file(self, content, hookname="test"): |
692 | + hook_file = os.path.join(self.hooks_dir, "%s.hook" % hookname) |
693 | + with mkfile(hook_file) as f: |
694 | + print(content, file=f) |
695 | + |
696 | def _setup_hooks_dir(self, preloads, hooks_dir=None): |
697 | if hooks_dir is None: |
698 | hooks_dir = self.temp_dir |
699 | preloads["click_get_hooks_dir"].side_effect = ( |
700 | lambda: self.make_string(hooks_dir)) |
701 | + self.hooks_dir = hooks_dir |
702 | |
703 | def g_spawn_sync_side_effect(self, status_map, working_directory, argv, |
704 | envp, flags, child_setup, user_data, |
705 | @@ -126,13 +151,12 @@ |
706 | "click_get_hooks_dir") as (enter, preloads): |
707 | enter() |
708 | self._setup_hooks_dir(preloads) |
709 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
710 | - print(dedent("""\ |
711 | + self._make_hook_file(dedent("""\ |
712 | Pattern: /usr/share/test/${id}.test |
713 | # Comment |
714 | Exec: test-update |
715 | User: root |
716 | - """), file=f) |
717 | + """)) |
718 | hook = Click.Hook.open(self.db, "test") |
719 | self.assertCountEqual( |
720 | ["pattern", "exec", "user"], hook.get_fields()) |
721 | @@ -146,8 +170,8 @@ |
722 | "click_get_hooks_dir") as (enter, preloads): |
723 | enter() |
724 | self._setup_hooks_dir(preloads) |
725 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
726 | - print("Pattern: /usr/share/test/${id}.test", file=f) |
727 | + self._make_hook_file( |
728 | + "Pattern: /usr/share/test/${id}.test") |
729 | hook = Click.Hook.open(self.db, "test") |
730 | self.assertEqual("test", hook.get_hook_name()) |
731 | |
732 | @@ -156,9 +180,10 @@ |
733 | "click_get_hooks_dir") as (enter, preloads): |
734 | enter() |
735 | self._setup_hooks_dir(preloads) |
736 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
737 | - print("Pattern: /usr/share/test/${id}.test", file=f) |
738 | - print("Hook-Name: other", file=f) |
739 | + self._make_hook_file(dedent("""\ |
740 | + Pattern: /usr/share/test/${id}.test |
741 | + Hook-Name: other""") |
742 | + ) |
743 | hook = Click.Hook.open(self.db, "test") |
744 | self.assertEqual("other", hook.get_hook_name()) |
745 | |
746 | @@ -167,13 +192,12 @@ |
747 | "click_get_hooks_dir") as (enter, preloads): |
748 | enter() |
749 | self._setup_hooks_dir(preloads) |
750 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
751 | - print(dedent("""\ |
752 | + self._make_hook_file(dedent("""\ |
753 | Pattern: /usr/share/test/${id}.test |
754 | # Comment |
755 | Exec: test-update |
756 | User: root |
757 | - """), file=f) |
758 | + """)) |
759 | hook = Click.Hook.open(self.db, "test") |
760 | self.assertRaisesHooksError( |
761 | Click.HooksError.BAD_APP_NAME, hook.get_app_id, |
762 | @@ -187,8 +211,8 @@ |
763 | "click_get_hooks_dir") as (enter, preloads): |
764 | enter() |
765 | self._setup_hooks_dir(preloads) |
766 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
767 | - print("Pattern: /usr/share/test/${short-id}.test", file=f) |
768 | + self._make_hook_file( |
769 | + "Pattern: /usr/share/test/${short-id}.test") |
770 | hook = Click.Hook.open(self.db, "test") |
771 | # It would perhaps be better if unrecognised $-expansions raised |
772 | # KeyError, but they don't right now. |
773 | @@ -201,9 +225,9 @@ |
774 | "click_get_hooks_dir") as (enter, preloads): |
775 | enter() |
776 | self._setup_hooks_dir(preloads) |
777 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
778 | - print("Pattern: /usr/share/test/${short-id}.test", file=f) |
779 | - print("Single-Version: yes", file=f) |
780 | + self._make_hook_file(dedent("""\ |
781 | + Pattern: /usr/share/test/${short-id}.test |
782 | + Single-Version: yes""")) |
783 | hook = Click.Hook.open(self.db, "test") |
784 | self.assertEqual( |
785 | "/usr/share/test/package_app-name.test", |
786 | @@ -231,8 +255,8 @@ |
787 | "click_get_hooks_dir") as (enter, preloads): |
788 | enter() |
789 | self._setup_hooks_dir(preloads) |
790 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
791 | - print("Pattern: %s/${id}.test" % self.temp_dir, file=f) |
792 | + self._make_hook_file( |
793 | + "Pattern: %s/${id}.test" % self.temp_dir) |
794 | os.makedirs( |
795 | os.path.join(self.temp_dir, "org.example.package", "1.0")) |
796 | hook = Click.Hook.open(self.db, "test") |
797 | @@ -251,8 +275,8 @@ |
798 | "click_get_hooks_dir") as (enter, preloads): |
799 | enter() |
800 | self._setup_hooks_dir(preloads) |
801 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
802 | - print("Pattern: %s/${id}/" % self.temp_dir, file=f) |
803 | + self._make_hook_file( |
804 | + "Pattern: %s/${id}/" % self.temp_dir) |
805 | os.makedirs( |
806 | os.path.join(self.temp_dir, "org.example.package", "1.0")) |
807 | hook = Click.Hook.open(self.db, "test") |
808 | @@ -276,8 +300,8 @@ |
809 | "click_get_hooks_dir") as (enter, preloads): |
810 | enter() |
811 | self._setup_hooks_dir(preloads) |
812 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
813 | - print("Pattern: %s/${id}.test" % self.temp_dir, file=f) |
814 | + self._make_hook_file( |
815 | + "Pattern: %s/${id}.test" % self.temp_dir) |
816 | underlay = os.path.join(self.temp_dir, "underlay") |
817 | overlay = os.path.join(self.temp_dir, "overlay") |
818 | db = Click.DB() |
819 | @@ -304,8 +328,8 @@ |
820 | "click_get_hooks_dir") as (enter, preloads): |
821 | enter() |
822 | self._setup_hooks_dir(preloads) |
823 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
824 | - print("Pattern: %s/${id}.test" % self.temp_dir, file=f) |
825 | + self._make_hook_file( |
826 | + "Pattern: %s/${id}.test" % self.temp_dir) |
827 | symlink_path = os.path.join( |
828 | self.temp_dir, "org.example.package_test-app_1.0.test") |
829 | os.symlink("old-target", symlink_path) |
830 | @@ -325,8 +349,8 @@ |
831 | "click_get_hooks_dir") as (enter, preloads): |
832 | enter() |
833 | self._setup_hooks_dir(preloads) |
834 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
835 | - print("Pattern: %s/${id}.test" % self.temp_dir, file=f) |
836 | + self._make_hook_file( |
837 | + "Pattern: %s/${id}.test" % self.temp_dir) |
838 | symlink_path = os.path.join( |
839 | self.temp_dir, "org.example.package_test-app_1.0.test") |
840 | os.symlink("old-target", symlink_path) |
841 | @@ -341,28 +365,20 @@ |
842 | enter() |
843 | self._setup_hooks_dir( |
844 | preloads, hooks_dir=os.path.join(self.temp_dir, "hooks")) |
845 | - with mkfile(os.path.join(self.temp_dir, "hooks", "new.hook")) as f: |
846 | - print("Pattern: %s/${id}.new" % self.temp_dir, file=f) |
847 | - with mkfile_utf8(os.path.join( |
848 | - self.temp_dir, "test-1", "1.0", ".click", "info", |
849 | - "test-1.manifest")) as f: |
850 | - json.dump({ |
851 | + self._make_hook_file( |
852 | + "Pattern: %s/${id}.new" % self.temp_dir, |
853 | + hookname="new") |
854 | + self._make_installed_click("test-1", "1.0", json_data={ |
855 | "maintainer": |
856 | b"Unic\xc3\xb3de <unicode@example.org>".decode( |
857 | "UTF-8"), |
858 | - "hooks": {"test1-app": {"new": "target-1"}}, |
859 | - }, f, ensure_ascii=False) |
860 | - os.symlink("1.0", os.path.join(self.temp_dir, "test-1", "current")) |
861 | - with mkfile_utf8(os.path.join( |
862 | - self.temp_dir, "test-2", "2.0", ".click", "info", |
863 | - "test-2.manifest")) as f: |
864 | - json.dump({ |
865 | + "hooks": {"test1-app": {"new": "target-1"}}}) |
866 | + self._make_installed_click("test-2", "2.0", json_data={ |
867 | "maintainer": |
868 | b"Unic\xc3\xb3de <unicode@example.org>".decode( |
869 | "UTF-8"), |
870 | "hooks": {"test1-app": {"new": "target-2"}}, |
871 | - }, f, ensure_ascii=False) |
872 | - os.symlink("2.0", os.path.join(self.temp_dir, "test-2", "current")) |
873 | + }) |
874 | hook = Click.Hook.open(self.db, "new") |
875 | hook.install(user_name=None) |
876 | path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.new") |
877 | @@ -382,22 +398,17 @@ |
878 | enter() |
879 | self._setup_hooks_dir( |
880 | preloads, hooks_dir=os.path.join(self.temp_dir, "hooks")) |
881 | - with mkfile(os.path.join(self.temp_dir, "hooks", "old.hook")) as f: |
882 | - print("Pattern: %s/${id}.old" % self.temp_dir, file=f) |
883 | - with mkfile(os.path.join( |
884 | - self.temp_dir, "test-1", "1.0", ".click", "info", |
885 | - "test-1.manifest")) as f: |
886 | - json.dump({"hooks": {"test1-app": {"old": "target-1"}}}, f) |
887 | - os.symlink("1.0", os.path.join(self.temp_dir, "test-1", "current")) |
888 | + self._make_hook_file( |
889 | + "Pattern: %s/${id}.old" % self.temp_dir, |
890 | + hookname="old") |
891 | + self._make_installed_click("test-1", "1.0", json_data={ |
892 | + "hooks": {"test1-app": {"old": "target-1"}}}) |
893 | path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.old") |
894 | os.symlink( |
895 | os.path.join(self.temp_dir, "test-1", "1.0", "target-1"), |
896 | path_1) |
897 | - with mkfile(os.path.join( |
898 | - self.temp_dir, "test-2", "2.0", ".click", "info", |
899 | - "test-2.manifest")) as f: |
900 | - json.dump({"hooks": {"test2-app": {"old": "target-2"}}}, f) |
901 | - os.symlink("2.0", os.path.join(self.temp_dir, "test-2", "current")) |
902 | + self._make_installed_click("test-2", "2.0", json_data={ |
903 | + "hooks": {"test2-app": {"old": "target-2"}}}) |
904 | path_2 = os.path.join(self.temp_dir, "test-2_test2-app_2.0.old") |
905 | os.symlink( |
906 | os.path.join(self.temp_dir, "test-2", "2.0", "target-2"), |
907 | @@ -413,23 +424,14 @@ |
908 | enter() |
909 | self._setup_hooks_dir( |
910 | preloads, hooks_dir=os.path.join(self.temp_dir, "hooks")) |
911 | - with mkfile(os.path.join( |
912 | - self.temp_dir, "hooks", "test.hook")) as f: |
913 | - print("Pattern: %s/${id}.test" % self.temp_dir, file=f) |
914 | - with mkfile(os.path.join( |
915 | - self.temp_dir, "test-1", "1.0", ".click", "info", |
916 | - "test-1.manifest")) as f: |
917 | - json.dump({"hooks": {"test1-app": {"test": "target-1"}}}, f) |
918 | - os.symlink("1.0", os.path.join(self.temp_dir, "test-1", "current")) |
919 | - with mkfile(os.path.join( |
920 | - self.temp_dir, "test-2", "1.0", ".click", "info", |
921 | - "test-2.manifest")) as f: |
922 | - json.dump({"hooks": {"test2-app": {"test": "target-2"}}}, f) |
923 | - with mkfile(os.path.join( |
924 | - self.temp_dir, "test-2", "1.1", ".click", "info", |
925 | - "test-2.manifest")) as f: |
926 | - json.dump({"hooks": {"test2-app": {"test": "target-2"}}}, f) |
927 | - os.symlink("1.1", os.path.join(self.temp_dir, "test-2", "current")) |
928 | + self._make_hook_file( |
929 | + "Pattern: %s/${id}.test" % self.temp_dir) |
930 | + self._make_installed_click("test-1", "1.0", json_data={ |
931 | + "hooks": {"test1-app": {"test": "target-1"}}}) |
932 | + self._make_installed_click("test-2", "1.0", make_current=False, |
933 | + json_data={"hooks": {"test2-app": {"test": "target-2"}}}) |
934 | + self._make_installed_click("test-2", "1.1", json_data={ |
935 | + "hooks": {"test2-app": {"test": "target-2"}}}) |
936 | path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.test") |
937 | os.symlink( |
938 | os.path.join(self.temp_dir, "test-1", "1.0", "target-1"), |
939 | @@ -465,13 +467,12 @@ |
940 | "click_get_hooks_dir") as (enter, preloads): |
941 | enter() |
942 | self._setup_hooks_dir(preloads) |
943 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
944 | - print(dedent("""\ |
945 | + self._make_hook_file(dedent("""\ |
946 | User-Level: yes |
947 | Pattern: ${home}/.local/share/test/${id}.test |
948 | # Comment |
949 | Exec: test-update |
950 | - """), file=f) |
951 | + """)) |
952 | hook = Click.Hook.open(self.db, "test") |
953 | self.assertCountEqual( |
954 | ["user-level", "pattern", "exec"], hook.get_fields()) |
955 | @@ -486,9 +487,10 @@ |
956 | "click_get_hooks_dir") as (enter, preloads): |
957 | enter() |
958 | self._setup_hooks_dir(preloads) |
959 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
960 | - print("User-Level: yes", file=f) |
961 | - print("Pattern: ${home}/.local/share/test/${id}.test", file=f) |
962 | + self._make_hook_file(dedent("""\ |
963 | + User-Level: yes |
964 | + Pattern: ${home}/.local/share/test/${id}.test""") |
965 | + ) |
966 | hook = Click.Hook.open(self.db, "test") |
967 | self.assertEqual("test", hook.get_hook_name()) |
968 | |
969 | @@ -497,10 +499,11 @@ |
970 | "click_get_hooks_dir") as (enter, preloads): |
971 | enter() |
972 | self._setup_hooks_dir(preloads) |
973 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
974 | - print("User-Level: yes", file=f) |
975 | - print("Pattern: ${home}/.local/share/test/${id}.test", file=f) |
976 | - print("Hook-Name: other", file=f) |
977 | + self._make_hook_file(dedent("""\ |
978 | + User-Level: yes |
979 | + Pattern: ${home}/.local/share/test/${id}.test |
980 | + Hook-Name: other""") |
981 | + ) |
982 | hook = Click.Hook.open(self.db, "test") |
983 | self.assertEqual("other", hook.get_hook_name()) |
984 | |
985 | @@ -509,13 +512,12 @@ |
986 | "click_get_hooks_dir") as (enter, preloads): |
987 | enter() |
988 | self._setup_hooks_dir(preloads) |
989 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
990 | - print(dedent("""\ |
991 | + self._make_hook_file(dedent("""\ |
992 | User-Level: yes |
993 | Pattern: ${home}/.local/share/test/${id}.test |
994 | # Comment |
995 | - Exec: test-update |
996 | - """), file=f) |
997 | + Exec: test-update""") |
998 | + ) |
999 | hook = Click.Hook.open(self.db, "test") |
1000 | self.assertRaisesHooksError( |
1001 | Click.HooksError.BAD_APP_NAME, hook.get_app_id, |
1002 | @@ -531,11 +533,10 @@ |
1003 | self._setup_hooks_dir(preloads) |
1004 | preloads["getpwnam"].side_effect = ( |
1005 | lambda name: self.make_pointer(Passwd(pw_dir=b"/mock"))) |
1006 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
1007 | - print("User-Level: yes", file=f) |
1008 | - print( |
1009 | - "Pattern: ${home}/.local/share/test/${short-id}.test", |
1010 | - file=f) |
1011 | + self._make_hook_file(dedent("""\ |
1012 | + User-Level: yes |
1013 | + Pattern: ${home}/.local/share/test/${short-id}.test |
1014 | + """)) |
1015 | hook = Click.Hook.open(self.db, "test") |
1016 | self.assertEqual( |
1017 | "/mock/.local/share/test/package_app-name.test", |
1018 | @@ -554,8 +555,8 @@ |
1019 | print("Exec: test-update", file=f) |
1020 | hook = Click.Hook.open(self.db, "test") |
1021 | self.assertEqual( |
1022 | - "test-user", hook.get_run_commands_user(user_name="test-user")) |
1023 | - hook.run_commands(user_name="test-user") |
1024 | + self.TEST_USER, hook.get_run_commands_user(user_name=self.TEST_USER)) |
1025 | + hook.run_commands(user_name=self.TEST_USER) |
1026 | self.assertEqual( |
1027 | [[b"/bin/sh", b"-c", b"test-update"]], self.spawn_calls) |
1028 | |
1029 | @@ -568,19 +569,20 @@ |
1030 | preloads["click_get_user_home"].return_value = "/home/test-user" |
1031 | os.makedirs(os.path.join( |
1032 | self.temp_dir, "org.example.package", "1.0")) |
1033 | - user_db = Click.User.for_user(self.db, "test-user") |
1034 | + user_db = Click.User.for_user(self.db, self.TEST_USER) |
1035 | user_db.set_version("org.example.package", "1.0") |
1036 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
1037 | - print("User-Level: yes", file=f) |
1038 | - print("Pattern: %s/${id}.test" % self.temp_dir, file=f) |
1039 | + self._make_hook_file(dedent("""\ |
1040 | + User-Level: yes |
1041 | + Pattern: %s/${id}.test""") % self.temp_dir |
1042 | + ) |
1043 | hook = Click.Hook.open(self.db, "test") |
1044 | hook.install_package( |
1045 | "org.example.package", "1.0", "test-app", "foo/bar", |
1046 | - user_name="test-user") |
1047 | + user_name=self.TEST_USER) |
1048 | symlink_path = os.path.join( |
1049 | self.temp_dir, "org.example.package_test-app_1.0.test") |
1050 | target_path = os.path.join( |
1051 | - self.temp_dir, ".click", "users", "test-user", |
1052 | + self.temp_dir, ".click", "users", self.TEST_USER, |
1053 | "org.example.package", "foo", "bar") |
1054 | self.assertTrue(os.path.islink(symlink_path)) |
1055 | self.assertEqual(target_path, os.readlink(symlink_path)) |
1056 | @@ -594,19 +596,20 @@ |
1057 | preloads["click_get_user_home"].return_value = "/home/test-user" |
1058 | os.makedirs(os.path.join( |
1059 | self.temp_dir, "org.example.package", "1.0")) |
1060 | - user_db = Click.User.for_user(self.db, "test-user") |
1061 | + user_db = Click.User.for_user(self.db, self.TEST_USER) |
1062 | user_db.set_version("org.example.package", "1.0") |
1063 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
1064 | - print("User-Level: yes", file=f) |
1065 | - print("Pattern: %s/${id}/" % self.temp_dir, file=f) |
1066 | + self._make_hook_file(dedent("""\ |
1067 | + User-Level: yes |
1068 | + Pattern: %s/${id}/""") % self.temp_dir |
1069 | + ) |
1070 | hook = Click.Hook.open(self.db, "test") |
1071 | hook.install_package( |
1072 | "org.example.package", "1.0", "test-app", "foo", |
1073 | - user_name="test-user") |
1074 | + user_name=self.TEST_USER) |
1075 | symlink_path = os.path.join( |
1076 | self.temp_dir, "org.example.package_test-app_1.0") |
1077 | target_path = os.path.join( |
1078 | - self.temp_dir, ".click", "users", "test-user", |
1079 | + self.temp_dir, ".click", "users", self.TEST_USER, |
1080 | "org.example.package", "foo") |
1081 | self.assertTrue(os.path.islink(symlink_path)) |
1082 | self.assertEqual(target_path, os.readlink(symlink_path)) |
1083 | @@ -622,18 +625,19 @@ |
1084 | self.temp_dir, "org.example.package", "1.0")) |
1085 | os.makedirs(os.path.join( |
1086 | self.temp_dir, "org.example.package", "1.1")) |
1087 | - user_db = Click.User.for_user(self.db, "test-user") |
1088 | + user_db = Click.User.for_user(self.db, self.TEST_USER) |
1089 | user_db.set_version("org.example.package", "1.0") |
1090 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
1091 | - print("User-Level: yes", file=f) |
1092 | - print("Pattern: %s/${id}.test" % self.temp_dir, file=f) |
1093 | + self._make_hook_file(dedent("""\ |
1094 | + User-Level: yes |
1095 | + Pattern: %s/${id}.test""") % self.temp_dir |
1096 | + ) |
1097 | hook = Click.Hook.open(self.db, "test") |
1098 | hook.install_package( |
1099 | "org.example.package", "1.0", "test-app", "foo/bar", |
1100 | - user_name="test-user") |
1101 | + user_name=self.TEST_USER) |
1102 | hook.install_package( |
1103 | "org.example.package", "1.1", "test-app", "foo/bar", |
1104 | - user_name="test-user") |
1105 | + user_name=self.TEST_USER) |
1106 | old_symlink_path = os.path.join( |
1107 | self.temp_dir, "org.example.package_test-app_1.0.test") |
1108 | symlink_path = os.path.join( |
1109 | @@ -641,7 +645,7 @@ |
1110 | self.assertFalse(os.path.islink(old_symlink_path)) |
1111 | self.assertTrue(os.path.islink(symlink_path)) |
1112 | target_path = os.path.join( |
1113 | - self.temp_dir, ".click", "users", "test-user", |
1114 | + self.temp_dir, ".click", "users", self.TEST_USER, |
1115 | "org.example.package", "foo", "bar") |
1116 | self.assertEqual(target_path, os.readlink(symlink_path)) |
1117 | |
1118 | @@ -657,17 +661,18 @@ |
1119 | os.symlink("old-target", symlink_path) |
1120 | os.makedirs(os.path.join( |
1121 | self.temp_dir, "org.example.package", "1.0")) |
1122 | - user_db = Click.User.for_user(self.db, "test-user") |
1123 | + user_db = Click.User.for_user(self.db, self.TEST_USER) |
1124 | user_db.set_version("org.example.package", "1.0") |
1125 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
1126 | - print("User-Level: yes", file=f) |
1127 | - print("Pattern: %s/${id}.test" % self.temp_dir, file=f) |
1128 | + self._make_hook_file(dedent("""\ |
1129 | + User-Level: yes |
1130 | + Pattern: %s/${id}.test""") % self.temp_dir |
1131 | + ) |
1132 | hook = Click.Hook.open(self.db, "test") |
1133 | hook.install_package( |
1134 | "org.example.package", "1.0", "test-app", "foo/bar", |
1135 | - user_name="test-user") |
1136 | + user_name=self.TEST_USER) |
1137 | target_path = os.path.join( |
1138 | - self.temp_dir, ".click", "users", "test-user", |
1139 | + self.temp_dir, ".click", "users", self.TEST_USER, |
1140 | "org.example.package", "foo", "bar") |
1141 | self.assertTrue(os.path.islink(symlink_path)) |
1142 | self.assertEqual(target_path, os.readlink(symlink_path)) |
1143 | @@ -679,16 +684,17 @@ |
1144 | enter() |
1145 | self._setup_hooks_dir(preloads) |
1146 | preloads["click_get_user_home"].return_value = "/home/test-user" |
1147 | - with mkfile(os.path.join(self.temp_dir, "test.hook")) as f: |
1148 | - print("User-Level: yes", file=f) |
1149 | - print("Pattern: %s/${id}.test" % self.temp_dir, file=f) |
1150 | + self._make_hook_file(dedent("""\ |
1151 | + User-Level: yes |
1152 | + Pattern: %s/${id}.test""") % self.temp_dir |
1153 | + ) |
1154 | symlink_path = os.path.join( |
1155 | self.temp_dir, "org.example.package_test-app_1.0.test") |
1156 | os.symlink("old-target", symlink_path) |
1157 | hook = Click.Hook.open(self.db, "test") |
1158 | hook.remove_package( |
1159 | "org.example.package", "1.0", "test-app", |
1160 | - user_name="test-user") |
1161 | + user_name=self.TEST_USER) |
1162 | self.assertFalse(os.path.exists(symlink_path)) |
1163 | |
1164 | def test_install(self): |
1165 | @@ -696,32 +702,26 @@ |
1166 | "click_get_hooks_dir", "click_get_user_home", |
1167 | ) as (enter, preloads): |
1168 | enter() |
1169 | + # Don't tell click about the hooks directory yet. |
1170 | self._setup_hooks_dir(preloads) |
1171 | preloads["click_get_user_home"].return_value = "/home/test-user" |
1172 | with mkfile(os.path.join(self.temp_dir, "hooks", "new.hook")) as f: |
1173 | print("User-Level: yes", file=f) |
1174 | print("Pattern: %s/${id}.new" % self.temp_dir, file=f) |
1175 | - user_db = Click.User.for_user(self.db, "test-user") |
1176 | - with mkfile_utf8(os.path.join( |
1177 | - self.temp_dir, "test-1", "1.0", ".click", "info", |
1178 | - "test-1.manifest")) as f: |
1179 | - json.dump({ |
1180 | + self._make_installed_click("test-1", "1.0", json_data={ |
1181 | "maintainer": |
1182 | b"Unic\xc3\xb3de <unicode@example.org>".decode( |
1183 | "UTF-8"), |
1184 | "hooks": {"test1-app": {"new": "target-1"}}, |
1185 | - }, f, ensure_ascii=False) |
1186 | - user_db.set_version("test-1", "1.0") |
1187 | - with mkfile_utf8(os.path.join( |
1188 | - self.temp_dir, "test-2", "2.0", ".click", "info", |
1189 | - "test-2.manifest")) as f: |
1190 | - json.dump({ |
1191 | + }) |
1192 | + self._make_installed_click("test-2", "2.0", json_data={ |
1193 | "maintainer": |
1194 | b"Unic\xc3\xb3de <unicode@example.org>".decode( |
1195 | "UTF-8"), |
1196 | "hooks": {"test1-app": {"new": "target-2"}}, |
1197 | - }, f, ensure_ascii=False) |
1198 | - user_db.set_version("test-2", "2.0") |
1199 | + }) |
1200 | + # Now tell click about the hooks directory and make sure it |
1201 | + # catches up correctly. |
1202 | self._setup_hooks_dir( |
1203 | preloads, hooks_dir=os.path.join(self.temp_dir, "hooks")) |
1204 | hook = Click.Hook.open(self.db, "new") |
1205 | @@ -730,14 +730,14 @@ |
1206 | self.assertTrue(os.path.lexists(path_1)) |
1207 | self.assertEqual( |
1208 | os.path.join( |
1209 | - self.temp_dir, ".click", "users", "test-user", "test-1", |
1210 | + self.temp_dir, ".click", "users", self.TEST_USER, "test-1", |
1211 | "target-1"), |
1212 | os.readlink(path_1)) |
1213 | path_2 = os.path.join(self.temp_dir, "test-2_test1-app_2.0.new") |
1214 | self.assertTrue(os.path.lexists(path_2)) |
1215 | self.assertEqual( |
1216 | os.path.join( |
1217 | - self.temp_dir, ".click", "users", "test-user", "test-2", |
1218 | + self.temp_dir, ".click", "users", self.TEST_USER, "test-2", |
1219 | "target-2"), |
1220 | os.readlink(path_2)) |
1221 | |
1222 | @@ -747,17 +747,17 @@ |
1223 | self.assertFalse(os.path.lexists(path_1)) |
1224 | self.assertFalse(os.path.lexists(path_2)) |
1225 | |
1226 | - hook.install(user_name="test-user") |
1227 | + hook.install(user_name=self.TEST_USER) |
1228 | self.assertTrue(os.path.lexists(path_1)) |
1229 | self.assertEqual( |
1230 | os.path.join( |
1231 | - self.temp_dir, ".click", "users", "test-user", "test-1", |
1232 | + self.temp_dir, ".click", "users", self.TEST_USER, "test-1", |
1233 | "target-1"), |
1234 | os.readlink(path_1)) |
1235 | self.assertTrue(os.path.lexists(path_2)) |
1236 | self.assertEqual( |
1237 | os.path.join( |
1238 | - self.temp_dir, ".click", "users", "test-user", "test-2", |
1239 | + self.temp_dir, ".click", "users", self.TEST_USER, "test-2", |
1240 | "target-2"), |
1241 | os.readlink(path_2)) |
1242 | |
1243 | @@ -766,29 +766,25 @@ |
1244 | "click_get_hooks_dir", "click_get_user_home", |
1245 | ) as (enter, preloads): |
1246 | enter() |
1247 | + # Don't tell click about the hooks directory yet. |
1248 | self._setup_hooks_dir(preloads) |
1249 | preloads["click_get_user_home"].return_value = "/home/test-user" |
1250 | with mkfile(os.path.join(self.temp_dir, "hooks", "old.hook")) as f: |
1251 | print("User-Level: yes", file=f) |
1252 | print("Pattern: %s/${id}.old" % self.temp_dir, file=f) |
1253 | - user_db = Click.User.for_user(self.db, "test-user") |
1254 | - with mkfile(os.path.join( |
1255 | - self.temp_dir, "test-1", "1.0", ".click", "info", |
1256 | - "test-1.manifest")) as f: |
1257 | - json.dump({"hooks": {"test1-app": {"old": "target-1"}}}, f) |
1258 | - user_db.set_version("test-1", "1.0") |
1259 | - os.symlink("1.0", os.path.join(self.temp_dir, "test-1", "current")) |
1260 | + user_db = Click.User.for_user(self.db, self.TEST_USER) |
1261 | + self._make_installed_click("test-1", "1.0", json_data={ |
1262 | + "hooks": {"test1-app": {"old": "target-1"}}}) |
1263 | path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.old") |
1264 | os.symlink( |
1265 | os.path.join(user_db.get_path("test-1"), "target-1"), path_1) |
1266 | - with mkfile(os.path.join( |
1267 | - self.temp_dir, "test-2", "2.0", ".click", "info", |
1268 | - "test-2.manifest")) as f: |
1269 | - json.dump({"hooks": {"test2-app": {"old": "target-2"}}}, f) |
1270 | - user_db.set_version("test-2", "2.0") |
1271 | + self._make_installed_click("test-2", "2.0", json_data={ |
1272 | + "hooks": {"test2-app": {"old": "target-2"}}}) |
1273 | path_2 = os.path.join(self.temp_dir, "test-2_test2-app_2.0.old") |
1274 | os.symlink( |
1275 | os.path.join(user_db.get_path("test-2"), "target-2"), path_2) |
1276 | + # Now tell click about the hooks directory and make sure it |
1277 | + # catches up correctly. |
1278 | self._setup_hooks_dir( |
1279 | preloads, hooks_dir=os.path.join(self.temp_dir, "hooks")) |
1280 | hook = Click.Hook.open(self.db, "old") |
1281 | @@ -807,44 +803,37 @@ |
1282 | os.path.join(self.temp_dir, "hooks", "test.hook")) as f: |
1283 | print("User-Level: yes", file=f) |
1284 | print("Pattern: %s/${id}.test" % self.temp_dir, file=f) |
1285 | - user_db = Click.User.for_user(self.db, "test-user") |
1286 | - with mkfile(os.path.join( |
1287 | - self.temp_dir, "test-1", "1.0", ".click", "info", |
1288 | - "test-1.manifest")) as f: |
1289 | - json.dump({"hooks": {"test1-app": {"test": "target-1"}}}, f) |
1290 | - user_db.set_version("test-1", "1.0") |
1291 | - with mkfile(os.path.join( |
1292 | - self.temp_dir, "test-2", "1.1", ".click", "info", |
1293 | - "test-2.manifest")) as f: |
1294 | - json.dump({"hooks": {"test2-app": {"test": "target-2"}}}, f) |
1295 | - user_db.set_version("test-2", "1.1") |
1296 | + self._make_installed_click("test-1", "1.0", json_data={ |
1297 | + "hooks": {"test1-app": {"test": "target-1"}}}) |
1298 | + self._make_installed_click("test-2", "1.1", json_data={ |
1299 | + "hooks": {"test2-app": {"test": "target-2"}}}) |
1300 | path_1 = os.path.join(self.temp_dir, "test-1_test1-app_1.0.test") |
1301 | os.symlink( |
1302 | os.path.join( |
1303 | - self.temp_dir, ".click", "users", "test-user", "test-1", |
1304 | + self.temp_dir, ".click", "users", self.TEST_USER, "test-1", |
1305 | "target-1"), |
1306 | path_1) |
1307 | path_2 = os.path.join(self.temp_dir, "test-2_test2-app_1.1.test") |
1308 | path_3 = os.path.join(self.temp_dir, "test-3_test3-app_1.0.test") |
1309 | os.symlink( |
1310 | os.path.join( |
1311 | - self.temp_dir, ".click", "users", "test-user", "test-3", |
1312 | + self.temp_dir, ".click", "users", self.TEST_USER, "test-3", |
1313 | "target-3"), |
1314 | path_3) |
1315 | self._setup_hooks_dir( |
1316 | preloads, hooks_dir=os.path.join(self.temp_dir, "hooks")) |
1317 | hook = Click.Hook.open(self.db, "test") |
1318 | - hook.sync(user_name="test-user") |
1319 | + hook.sync(user_name=self.TEST_USER) |
1320 | self.assertTrue(os.path.lexists(path_1)) |
1321 | self.assertEqual( |
1322 | os.path.join( |
1323 | - self.temp_dir, ".click", "users", "test-user", "test-1", |
1324 | + self.temp_dir, ".click", "users", self.TEST_USER, "test-1", |
1325 | "target-1"), |
1326 | os.readlink(path_1)) |
1327 | self.assertTrue(os.path.lexists(path_2)) |
1328 | self.assertEqual( |
1329 | os.path.join( |
1330 | - self.temp_dir, ".click", "users", "test-user", "test-2", |
1331 | + self.temp_dir, ".click", "users", self.TEST_USER, "test-2", |
1332 | "target-2"), |
1333 | os.readlink(path_2)) |
1334 | self.assertFalse(os.path.lexists(path_3)) |
1335 | @@ -859,18 +848,15 @@ |
1336 | os.path.join(self.temp_dir, "hooks", "test.hook")) as f: |
1337 | print("User-Level: yes", file=f) |
1338 | print("Pattern: %s/${id}.test" % self.temp_dir, file=f) |
1339 | - with mkfile(os.path.join( |
1340 | - self.temp_dir, "test-package", "1.0", ".click", "info", |
1341 | - "test-package.manifest")) as f: |
1342 | - json.dump({"hooks": {"test-app": {"test": "target"}}}, f) |
1343 | - all_users_db = Click.User.for_all_users(self.db) |
1344 | - all_users_db.set_version("test-package", "1.0") |
1345 | + self._make_installed_click( |
1346 | + "test-package", "1.0", all_users=True, json_data={ |
1347 | + "hooks": {"test-app": {"test": "target"}}}) |
1348 | self._setup_hooks_dir( |
1349 | preloads, hooks_dir=os.path.join(self.temp_dir, "hooks")) |
1350 | hook = Click.Hook.open(self.db, "test") |
1351 | - hook.sync(user_name="test-user") |
1352 | + hook.sync(user_name=self.TEST_USER) |
1353 | self.assertFalse(os.path.exists(os.path.join( |
1354 | - self.temp_dir, ".click", "users", "test-user", |
1355 | + self.temp_dir, ".click", "users", self.TEST_USER, |
1356 | "test-package"))) |
1357 | |
1358 | def test_sync_uses_deepest_copy(self): |
1359 | @@ -911,7 +897,7 @@ |
1360 | underlay_user_link = os.path.join( |
1361 | underlay, ".click", "users", "@all", "test-package") |
1362 | overlay_user_link = os.path.join( |
1363 | - overlay, ".click", "users", "test-user", "test-package") |
1364 | + overlay, ".click", "users", self.TEST_USER, "test-package") |
1365 | Click.ensuredir(os.path.dirname(underlay_user_link)) |
1366 | os.symlink(underlay_unpacked, underlay_user_link) |
1367 | Click.ensuredir(os.path.dirname(overlay_user_link)) |
1368 | @@ -922,7 +908,7 @@ |
1369 | overlay_target_path = os.path.join(overlay_user_link, "foo") |
1370 | os.symlink(overlay_target_path, symlink_path) |
1371 | hook = Click.Hook.open(db, "test") |
1372 | - hook.sync(user_name="test-user") |
1373 | + hook.sync(user_name=self.TEST_USER) |
1374 | self.assertTrue(os.path.islink(underlay_user_link)) |
1375 | self.assertEqual( |
1376 | underlay_unpacked, os.readlink(underlay_user_link)) |
1377 | @@ -962,17 +948,9 @@ |
1378 | yelp_other_path = os.path.join( |
1379 | self.temp_dir, "yelp", "other-test_app_1.0.txt") |
1380 | os.symlink("dummy", yelp_other_path) |
1381 | - package_dir = os.path.join(self.temp_dir, "test") |
1382 | - with mkfile(os.path.join( |
1383 | - package_dir, "1.0", ".click", "info", |
1384 | - "test.manifest")) as f: |
1385 | - json.dump( |
1386 | - {"hooks": {"app": {"yelp": "foo.txt", "unity": "foo.scope"}}}, |
1387 | - f) |
1388 | - with mkfile(os.path.join( |
1389 | - package_dir, "1.1", ".click", "info", |
1390 | - "test.manifest")) as f: |
1391 | - json.dump({}, f) |
1392 | + self._make_installed_click("test", "1.0", make_current=False, json_data={ |
1393 | + "hooks": {"app": {"yelp": "foo.txt", "unity": "foo.scope"}}}) |
1394 | + self._make_installed_click("test", "1.1", json_data={}) |
1395 | Click.package_install_hooks( |
1396 | self.db, "test", "1.0", "1.1", user_name=None) |
1397 | self.assertFalse(os.path.lexists(unity_path)) |
1398 | @@ -995,15 +973,10 @@ |
1399 | print("Hook-Name: b", file=f) |
1400 | os.mkdir(os.path.join(self.temp_dir, "a")) |
1401 | os.mkdir(os.path.join(self.temp_dir, "b")) |
1402 | - package_dir = os.path.join(self.temp_dir, "test") |
1403 | - with mkfile(os.path.join( |
1404 | - package_dir, "1.0", ".click", "info", |
1405 | - "test.manifest")) as f: |
1406 | - json.dump({"hooks": {}}, f) |
1407 | - with mkfile(os.path.join( |
1408 | - package_dir, "1.1", ".click", "info", |
1409 | - "test.manifest")) as f: |
1410 | - json.dump({"hooks": {"app": {"a": "foo.a", "b": "foo.b"}}}, f) |
1411 | + self._make_installed_click("test", "1.0", make_current=False, |
1412 | + json_data={"hooks": {}}) |
1413 | + self._make_installed_click("test", "1.1", json_data={ |
1414 | + "hooks": {"app": {"a": "foo.a", "b": "foo.b"}}}) |
1415 | Click.package_install_hooks( |
1416 | self.db, "test", "1.0", "1.1", user_name=None) |
1417 | self.assertTrue(os.path.lexists( |
1418 | @@ -1113,3 +1086,95 @@ |
1419 | self.assertFalse(os.path.lexists(unity_path)) |
1420 | self.assertFalse(os.path.lexists(yelp_docs_path)) |
1421 | self.assertFalse(os.path.lexists(yelp_other_path)) |
1422 | + |
1423 | + |
1424 | +class TestPackageHooksValidateFramework(TestClickHookBase): |
1425 | + |
1426 | + def _setup_test_env(self, preloads): |
1427 | + preloads["click_get_user_home"].return_value = "/home/test-user" |
1428 | + self._setup_hooks_dir( |
1429 | + preloads, os.path.join(self.temp_dir, "hooks")) |
1430 | + self._make_hook_file(dedent("""\ |
1431 | + User-Level: yes |
1432 | + Pattern: %s/${id}.test |
1433 | + """) % self.temp_dir) |
1434 | + self.hook_symlink_path = os.path.join( |
1435 | + self.temp_dir, "test-1_test1-app_1.0.test") |
1436 | + |
1437 | + def test_links_are_kept_on_validate_framework(self): |
1438 | + with self.run_in_subprocess( |
1439 | + "click_get_hooks_dir", "click_get_user_home", |
1440 | + "click_get_frameworks_dir", |
1441 | + ) as (enter, preloads): |
1442 | + enter() |
1443 | + self._setup_frameworks( |
1444 | + preloads, frameworks=["ubuntu-sdk-13.10"]) |
1445 | + self._setup_test_env(preloads) |
1446 | + self._make_installed_click(json_data={ |
1447 | + "framework": "ubuntu-sdk-13.10", |
1448 | + "hooks": { |
1449 | + "test1-app": {"test": "target-1"} |
1450 | + }, |
1451 | + }) |
1452 | + self.assertTrue(os.path.lexists(self.hook_symlink_path)) |
1453 | + # run the hooks |
1454 | + Click.run_user_hooks(self.db, user_name=self.TEST_USER) |
1455 | + self.assertTrue(os.path.lexists(self.hook_symlink_path)) |
1456 | + |
1457 | + def test_links_are_kept_multiple_frameworks(self): |
1458 | + with self.run_in_subprocess( |
1459 | + "click_get_hooks_dir", "click_get_user_home", |
1460 | + "click_get_frameworks_dir", |
1461 | + ) as (enter, preloads): |
1462 | + enter() |
1463 | + self._setup_frameworks( |
1464 | + preloads, frameworks=["ubuntu-sdk-14.04", "ubuntu-sdk-13.10"]) |
1465 | + self._setup_test_env(preloads) |
1466 | + self._make_installed_click(json_data={ |
1467 | + "framework": "ubuntu-sdk-13.10", |
1468 | + "hooks": { |
1469 | + "test1-app": {"test": "target-1"} |
1470 | + }, |
1471 | + }) |
1472 | + self.assertTrue(os.path.lexists(self.hook_symlink_path)) |
1473 | + # run the hooks |
1474 | + Click.run_user_hooks(self.db, user_name=self.TEST_USER) |
1475 | + self.assertTrue(os.path.lexists(self.hook_symlink_path)) |
1476 | + |
1477 | + def test_links_are_removed_on_missing_framework(self): |
1478 | + with self.run_in_subprocess( |
1479 | + "click_get_hooks_dir", "click_get_user_home", |
1480 | + "click_get_frameworks_dir", |
1481 | + ) as (enter, preloads): |
1482 | + enter() |
1483 | + self._setup_frameworks(preloads, frameworks=["missing"]) |
1484 | + self._setup_test_env(preloads) |
1485 | + self._make_installed_click(json_data={ |
1486 | + "framework": "ubuntu-sdk-13.10", |
1487 | + "hooks": { |
1488 | + "test1-app": {"test": "target-1"} |
1489 | + }, |
1490 | + }) |
1491 | + self.assertTrue(os.path.lexists(self.hook_symlink_path)) |
1492 | + # run the hooks |
1493 | + Click.run_user_hooks(self.db, user_name=self.TEST_USER) |
1494 | + self.assertFalse(os.path.lexists(self.hook_symlink_path)) |
1495 | + |
1496 | + def test_links_are_removed_on_missing_multiple_framework(self): |
1497 | + with self.run_in_subprocess( |
1498 | + "click_get_hooks_dir", "click_get_user_home", |
1499 | + "click_get_frameworks_dir", |
1500 | + ) as (enter, preloads): |
1501 | + enter() |
1502 | + self._setup_frameworks(preloads, frameworks=["ubuntu-sdk-13.10"]) |
1503 | + self._setup_test_env(preloads) |
1504 | + self._make_installed_click(json_data={ |
1505 | + "framework": "ubuntu-sdk-13.10, ubuntu-sdk-13.10-html", |
1506 | + "hooks": { |
1507 | + "test1-app": {"test": "target-1"} |
1508 | + }, |
1509 | + }) |
1510 | + self.assertTrue(os.path.lexists(self.hook_symlink_path)) |
1511 | + # run the hooks |
1512 | + Click.run_user_hooks(self.db, user_name=self.TEST_USER) |
1513 | + self.assertFalse(os.path.lexists(self.hook_symlink_path)) |
1514 | |
1515 | === modified file 'click/tests/test_install.py' |
1516 | --- click/tests/test_install.py 2014-05-05 13:23:35 +0000 |
1517 | +++ click/tests/test_install.py 2014-05-20 13:13:48 +0000 |
1518 | @@ -43,7 +43,13 @@ |
1519 | ClickInstallerPermissionDenied, |
1520 | ) |
1521 | from click.preinst import static_preinst |
1522 | -from click.tests.helpers import TestCase, mkfile, mock, touch |
1523 | +from click.tests.helpers import ( |
1524 | + disable_logging, |
1525 | + mkfile, |
1526 | + mock, |
1527 | + TestCase, |
1528 | + touch, |
1529 | +) |
1530 | |
1531 | |
1532 | @contextmanager |
1533 | @@ -103,14 +109,6 @@ |
1534 | self.temp_dir, control_dir, data_dir, package_path) |
1535 | return package_path |
1536 | |
1537 | - def _setup_frameworks(self, preloads, frameworks_dir=None, frameworks=[]): |
1538 | - frameworks_dir = self._create_mock_framework_dir(frameworks_dir) |
1539 | - shutil.rmtree(frameworks_dir, ignore_errors=True) |
1540 | - for framework in frameworks: |
1541 | - self._create_mock_framework_file(framework) |
1542 | - preloads["click_get_frameworks_dir"].side_effect = ( |
1543 | - lambda: self.make_string(frameworks_dir)) |
1544 | - |
1545 | def test_audit_no_click_version(self): |
1546 | path = self.make_fake_package() |
1547 | self.assertRaisesRegex( |
1548 | @@ -234,6 +232,7 @@ |
1549 | 'Framework "missing" not present on system.*', |
1550 | ClickInstaller(self.db).audit, path) |
1551 | |
1552 | + @disable_logging |
1553 | def test_audit_missing_framework_force(self): |
1554 | with self.run_in_subprocess( |
1555 | "click_get_frameworks_dir") as (enter, preloads): |
1556 | @@ -659,6 +658,7 @@ |
1557 | self.assertTrue( |
1558 | os.path.exists(os.path.join(root, "test-package", "current"))) |
1559 | |
1560 | + @disable_logging |
1561 | def test_reinstall_preinstalled(self): |
1562 | # Attempting to reinstall a preinstalled version shouldn't actually |
1563 | # reinstall it in an overlay database (which would cause |
1564 | |
1565 | === modified file 'debian/changelog' |
1566 | --- debian/changelog 2014-05-14 06:28:34 +0000 |
1567 | +++ debian/changelog 2014-05-20 13:13:48 +0000 |
1568 | @@ -1,3 +1,41 @@ |
1569 | +click (0.4.23.1) UNRELEASED; urgency=medium |
1570 | + |
1571 | + * chroot: Force dpkg-architecture to recalculate everything rather than |
1572 | + picking up values from the environment, to avoid the test suite getting |
1573 | + confused by environment variables exported by dpkg-buildpackage. |
1574 | + |
1575 | + -- Colin Watson <cjwatson@ubuntu.com> Tue, 20 May 2014 14:12:17 +0100 |
1576 | + |
1577 | +click (0.4.23) utopic; urgency=medium |
1578 | + |
1579 | + [ Michael Vogt ] |
1580 | + * Show human-readable error message when a click chroot subcommand fails |
1581 | + because of existing or non-existing chroots (LP: #1296820). |
1582 | + * Selectively disable logging on some tests to avoid message spam during |
1583 | + the test runs. |
1584 | + * When running hooks, remove hook symlinks if framework requirements are |
1585 | + not met (LP: #1271944). |
1586 | + * Cleanup the chroot if "click chroot create" fails (unless |
1587 | + --keep-broken-chroot is used) |
1588 | + * Fix sources.list generation when native_arch and target_arch are on the |
1589 | + same archive server (part of LP #1319153). |
1590 | + * Add "click framework list" command to list available frameworks |
1591 | + (LP: #1294659). |
1592 | + |
1593 | + [ Pete Woods ] |
1594 | + * Add libunity-scopes-dev package to chroot (LP: #1320786). |
1595 | + |
1596 | + [ Sergio Schvezov ] |
1597 | + * click chroot creation depends on dpkg-architecture, so recommend |
1598 | + dpkg-dev. |
1599 | + |
1600 | + [ Colin Watson ] |
1601 | + * chroot: Handle the case where we can execute binaries for the target |
1602 | + architecture directly and thus don't need a cross-compiler |
1603 | + (LP: #1319153). |
1604 | + |
1605 | + -- Colin Watson <cjwatson@ubuntu.com> Tue, 20 May 2014 14:10:11 +0100 |
1606 | + |
1607 | click (0.4.22) utopic; urgency=medium |
1608 | |
1609 | [ Michael Vogt ] |
1610 | |
1611 | === modified file 'debian/control' |
1612 | --- debian/control 2014-03-13 13:56:57 +0000 |
1613 | +++ debian/control 2014-05-20 13:13:48 +0000 |
1614 | @@ -28,7 +28,7 @@ |
1615 | Architecture: any |
1616 | Multi-Arch: foreign |
1617 | Depends: ${misc:Depends}, ${perl:Depends}, python3-click (= ${binary:Version}) |
1618 | -Recommends: debootstrap, schroot |
1619 | +Recommends: debootstrap, schroot, dpkg-dev |
1620 | Description: build Click packages |
1621 | Click is a simplified packaging format that installs in a separate part of |
1622 | the file system, suitable for third-party applications. |
1623 | |
1624 | === modified file 'doc/index.rst' |
1625 | --- doc/index.rst 2014-05-08 12:49:11 +0000 |
1626 | +++ doc/index.rst 2014-05-20 13:13:48 +0000 |
1627 | @@ -38,8 +38,10 @@ |
1628 | Then run:: |
1629 | |
1630 | $ ./autogen.sh |
1631 | - $ ./configure --with-systemdsystemunitdir=/lib/systemd/system \ |
1632 | - --with-systemduserunitdir=/usr/lib/systemd/user |
1633 | + $ ./configure --prefix=/usr \ |
1634 | + --sysconfdir=/etc \ |
1635 | + --with-systemdsystemunitdir=/lib/systemd/system \ |
1636 | + --with-systemduserunitdir=/usr/lib/systemd/user |
1637 | $ make |
1638 | |
1639 | to build the project. |
1640 | |
1641 | === modified file 'doc/manpage.rst' |
1642 | --- doc/manpage.rst 2014-03-18 11:47:21 +0000 |
1643 | +++ doc/manpage.rst 2014-05-20 13:13:48 +0000 |
1644 | @@ -35,6 +35,7 @@ |
1645 | click buildsource DIRECTORY |
1646 | click chroot |
1647 | click contents PATH |
1648 | + click framework list |
1649 | click hook install HOOK |
1650 | click hook remove HOOK |
1651 | click hook run-system |
1652 | @@ -148,6 +149,11 @@ |
1653 | |
1654 | Display the contents of the Click package in PATH as a file listing. |
1655 | |
1656 | +click framework list |
1657 | +-------------------- |
1658 | + |
1659 | +Display a list of available frameworks as one framework per line. |
1660 | + |
1661 | click hook install HOOK |
1662 | ----------------------- |
1663 | |
1664 | |
1665 | === modified file 'lib/click/hooks.vala' |
1666 | --- lib/click/hooks.vala 2014-04-08 09:31:40 +0000 |
1667 | +++ lib/click/hooks.vala 2014-05-20 13:13:48 +0000 |
1668 | @@ -56,9 +56,57 @@ |
1669 | INCOMPLETE |
1670 | } |
1671 | |
1672 | + |
1673 | +/* vala implementation of click.framework.validate_framework() |
1674 | + * |
1675 | + * Note that the required_frameworks string has the form |
1676 | + * framework1, framework2, ... |
1677 | + * See doc/file-format.rst for details. |
1678 | + */ |
1679 | +private bool |
1680 | +validate_framework (string required_frameworks) |
1681 | +{ |
1682 | + // valid framework names, cf. debian policy ยง5.6.1 |
1683 | + Regex valid_framework_re; |
1684 | + try { |
1685 | + valid_framework_re = new Regex ("^[a-z][a-z0-9.+-]+"); |
1686 | + } catch (RegexError e) { |
1687 | + error ("Could not compile regex /^[a-z][a-z0-9.+-]+/: %s", |
1688 | + e.message); |
1689 | + } |
1690 | + var base_version = ""; |
1691 | + foreach (var framework_name in required_frameworks.split (",")) |
1692 | + { |
1693 | + framework_name = framework_name.strip (); |
1694 | + if (!valid_framework_re.match (framework_name)) |
1695 | + return false; |
1696 | + Framework framework; |
1697 | + // now check the base-version |
1698 | + try { |
1699 | + framework = Framework.open (framework_name); |
1700 | + } catch (FrameworkError e) { |
1701 | + return false; |
1702 | + } |
1703 | + if (base_version == "") |
1704 | + base_version = framework.get_base_version (); |
1705 | + if (base_version != framework.get_base_version ()) |
1706 | + return false; |
1707 | + } |
1708 | + return true; |
1709 | +} |
1710 | + |
1711 | +private bool |
1712 | +validate_framework_for_package (DB db, string package, string? version) |
1713 | +{ |
1714 | + var manifest = read_manifest (db, package, version); |
1715 | + if (!manifest.has_member ("framework")) |
1716 | + return true; |
1717 | + var required_frameworks = manifest.get_string_member ("framework"); |
1718 | + return validate_framework (required_frameworks); |
1719 | +} |
1720 | + |
1721 | private Json.Object |
1722 | -read_manifest_hooks (DB db, string package, string? version) |
1723 | - throws DatabaseError |
1724 | +read_manifest (DB db, string package, string? version) |
1725 | { |
1726 | if (version == null) |
1727 | return new Json.Object (); |
1728 | @@ -69,15 +117,22 @@ |
1729 | @"$package.manifest"); |
1730 | parser.load_from_file (manifest_path); |
1731 | var manifest = parser.get_root ().get_object (); |
1732 | - if (! manifest.has_member ("hooks")) |
1733 | - return new Json.Object (); |
1734 | - var hooks = manifest.get_object_member ("hooks"); |
1735 | - return hooks.ref (); |
1736 | + return manifest.ref (); |
1737 | } catch (Error e) { |
1738 | return new Json.Object (); |
1739 | } |
1740 | } |
1741 | |
1742 | +private Json.Object |
1743 | +read_manifest_hooks (DB db, string package, string? version) |
1744 | +{ |
1745 | + var manifest = read_manifest (db, package, version); |
1746 | + if (! manifest.has_member ("hooks")) |
1747 | + return new Json.Object (); |
1748 | + var hooks = manifest.get_object_member ("hooks"); |
1749 | + return hooks.ref (); |
1750 | +} |
1751 | + |
1752 | private class PreviousEntry : Object, Gee.Hashable<PreviousEntry> { |
1753 | public string path { get; construct; } |
1754 | public string package { get; construct; } |
1755 | @@ -889,10 +944,16 @@ |
1756 | var ret = new List<RelevantApp> (); |
1757 | var hook_name = get_hook_name (); |
1758 | foreach (var unpacked in get_all_packages (user_name)) { |
1759 | - var manifest = read_manifest_hooks |
1760 | + // if the app is not using a valid framework (anymore) |
1761 | + // we don't consider it relevant (anymore) |
1762 | + if (!validate_framework_for_package |
1763 | + (db, unpacked.package, unpacked.version)) |
1764 | + continue; |
1765 | + |
1766 | + var manifest_hooks = read_manifest_hooks |
1767 | (db, unpacked.package, unpacked.version); |
1768 | - foreach (var app_name in manifest.get_members ()) { |
1769 | - var hooks = manifest.get_object_member |
1770 | + foreach (var app_name in manifest_hooks.get_members ()) { |
1771 | + var hooks = manifest_hooks.get_object_member |
1772 | (app_name); |
1773 | if (hooks.has_member (hook_name)) { |
1774 | var relative_path = hooks.get_string_member |
1775 | @@ -960,6 +1021,7 @@ |
1776 | unowned string package = app.package; |
1777 | unowned string version = app.version; |
1778 | unowned string app_name = app.app_name; |
1779 | + |
1780 | seen.add (@"$(package)_$(app_name)_$(version)"); |
1781 | if (is_user_level) { |
1782 | var user_db = new User.for_user |
1783 | @@ -1016,7 +1078,7 @@ |
1784 | * @new_version: The new version of the package. |
1785 | * @user_name: (allow-none): A user name, or null. |
1786 | * |
1787 | - * Run hooks following removal of a Click package. |
1788 | + * Run hooks following install of a Click package. |
1789 | * |
1790 | * If @user_name is null, only run system-level hooks. If @user_name is not |
1791 | * null, only run user-level hooks for that user. |