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