Merge lp:click/devel into lp:click

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 492
Merged at revision: 440
Proposed branch: lp:click/devel
Merge into: lp:click
Diff against target: 970 lines (+531/-55)
15 files modified
.bzrignore (+1/-0)
click/chroot.py (+42/-15)
click/tests/Makefile.am (+10/-0)
click/tests/test_database.py (+212/-15)
click/tests/test_framework.py (+43/-1)
click/tests/test_hooks.py (+58/-1)
click/tests/test_osextras.py (+30/-0)
click/tests/test_paths.py.in (+42/-0)
click/tests/test_user.py (+53/-2)
debian/changelog (+15/-0)
lib/click/user.vala (+4/-0)
tests/integration/helpers.py (+10/-8)
tests/integration/test_build_core_apps.py (+3/-6)
tests/integration/test_chroot.py (+1/-0)
tests/integration/test_hook.py (+7/-7)
To merge this branch: bzr merge lp:click/devel
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
click hackers Pending
Review via email: mp+229879@code.launchpad.net

Commit message

Click 0.4.30: Unit test improvements; fix upgrade if packages are present for removed users; flesh out 14.10 chroots.

Description of the change

  [ Colin Watson ]
  * Add many more unit tests to fill in some gaps in the coverage report.

  [ Michael Vogt ]
  * Exclude non-existing users from User.get_user_names()
    (LP: #1334611)

  [ Zoltan Balogh ]
  * Add a set of APIs to the 14.10 frameworks. Add ubuntu-ui-toolkit-doc to
    all frameworks.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2014-06-30 16:48:31 +0000
3+++ .bzrignore 2014-08-06 23:22:03 +0000
4@@ -33,6 +33,7 @@
5 click/paths.py
6 click/tests/config.py
7 click/tests/preload.gir
8+click/tests/test_paths.py
9 build-aux/compile
10 build-aux/config.guess
11 build-aux/config.rpath
12
13=== modified file 'click/chroot.py'
14--- click/chroot.py 2014-07-03 11:03:24 +0000
15+++ click/chroot.py 2014-08-06 23:22:03 +0000
16@@ -73,6 +73,7 @@
17 "qtscript5-dev:TARGET",
18 "qtsensors5-dev:TARGET",
19 "qttools5-dev:TARGET",
20+ "ubuntu-ui-toolkit-doc",
21 ],
22 "ubuntu-sdk-14.04": [
23 "cmake",
24@@ -94,27 +95,53 @@
25 "qtsensors5-dev:TARGET",
26 "qttools5-dev:TARGET",
27 "qttools5-dev-tools:TARGET",
28+ "ubuntu-ui-toolkit-doc",
29 ],
30 "ubuntu-sdk-14.10": [
31 "cmake",
32- "libqt5svg5-dev:TARGET",
33- "libqt5webkit5-dev:TARGET",
34- "libqt5xmlpatterns5-dev:TARGET",
35- "libunity-scopes-dev:TARGET",
36- # bug #1316930, needed for autopilot
37- "python3",
38- "qmlscene:TARGET",
39- "qt3d5-dev:TARGET",
40- "qt5-default:TARGET",
41- "qtbase5-dev:TARGET",
42- "qtdeclarative5-dev:TARGET",
43- "qtdeclarative5-dev-tools",
44- "qtlocation5-dev:TARGET",
45- "qtmultimedia5-dev:TARGET",
46- "qtscript5-dev:TARGET",
47+ "libcontent-hub-dev:TARGET",
48+ "libqt5keychain0:TARGET",
49 "libqt5sensors5-dev:TARGET",
50+ "libqt5svg5-dev:TARGET",
51+ "libqt5webkit5-dev:TARGET",
52+ "libqt5xmlpatterns5-dev:TARGET",
53+ "libunity-scopes-dev:TARGET",
54+ # bug #1316930, needed for autopilot
55+ "python3",
56+ "qml-module-qt-labs-settings:TARGET",
57+ "qml-module-qtmultimedia:TARGET",
58+ "qml-module-qtquick-layouts:TARGET",
59+ "qml-module-qtsensors:TARGET",
60+ "qml-module-qtwebkit:TARGET",
61+ "qmlscene:TARGET",
62+ "qt3d5-dev:TARGET",
63+ "qt5-default:TARGET",
64+ "qtdeclarative5-accounts-plugin:TARGET",
65+ "qtdeclarative5-dev-tools",
66+ "qtdeclarative5-folderlistmodel-plugin:TARGET",
67+ "qtdeclarative5-localstorage-plugin:TARGET",
68+ "qtdeclarative5-online-accounts-client0.1:TARGET",
69+ "qtdeclarative5-particles-plugin:TARGET",
70+ "qtdeclarative5-poppler1.0:TARGET",
71+ "qtdeclarative5-qtlocation-plugin:TARGET",
72+ "qtdeclarative5-qtorganizer-plugin:TARGET",
73+ "qtdeclarative5-qtpositioning-plugin:TARGET",
74+ "qtdeclarative5-u1db1.0:TARGET",
75+ "qtdeclarative5-ubuntu-content0.1:TARGET",
76+ "qtdeclarative5-ubuntu-download-manager0.1:TARGET",
77+ "qtdeclarative5-ubuntu-mediascanner0.1:TARGET",
78+ "qtdeclarative5-ubuntu-syncmonitor0.1:TARGET",
79+ "qtdeclarative5-ubuntu-telephony-phonenumber0.1:TARGET",
80+ "qtdeclarative5-ubuntu-ui-toolkit-plugin:TARGET",
81+ "qtdeclarative5-usermetrics0.1:TARGET",
82+ "qtdeclarative5-xmllistmodel-plugin:TARGET",
83+ "qtlocation5-dev:TARGET",
84+ "qtmultimedia5-dev:TARGET",
85+ "qtscript5-dev:TARGET",
86 "qttools5-dev:TARGET",
87 "qttools5-dev-tools:TARGET",
88+ "ubuntu-html5-theme:TARGET",
89+ "ubuntu-ui-toolkit-doc",
90 ],
91 }
92
93
94=== modified file 'click/tests/Makefile.am'
95--- click/tests/Makefile.am 2014-03-10 09:30:31 +0000
96+++ click/tests/Makefile.am 2014-08-06 23:22:03 +0000
97@@ -9,3 +9,13 @@
98 -I$(top_builddir)/lib/click -L$(top_builddir)/lib/click \
99 --accept-unprefixed --warn-all \
100 $< --output $@
101+
102+noinst_SCRIPTS = test_paths.py
103+CLEANFILES += $(noinst_SCRIPTS)
104+
105+do_subst = sed \
106+ -e 's,[@]sysconfdir[@],$(sysconfdir),g' \
107+ -e 's,[@]pkgdatadir[@],$(pkgdatadir),g'
108+
109+test_paths.py: test_paths.py.in Makefile
110+ $(do_subst) < $(srcdir)/test_paths.py.in > $@
111
112=== modified file 'click/tests/test_database.py'
113--- click/tests/test_database.py 2014-06-01 12:45:10 +0000
114+++ click/tests/test_database.py 2014-08-06 23:22:03 +0000
115@@ -20,6 +20,7 @@
116 __metaclass__ = type
117 __all__ = [
118 "TestClickDB",
119+ "TestClickInstalledPackage",
120 "TestClickSingleDB",
121 ]
122
123@@ -28,14 +29,50 @@
124 from itertools import takewhile
125 import json
126 import os
127+import unittest
128
129-from gi.repository import Click
130+from gi.repository import Click, GLib
131
132 from click.json_helpers import json_array_to_python, json_object_to_python
133 from click.tests.gimock_types import Passwd
134 from click.tests.helpers import TestCase, mkfile, touch
135
136
137+class TestClickInstalledPackage(TestCase):
138+ def setUp(self):
139+ super(TestClickInstalledPackage, self).setUp()
140+ self.foo = Click.InstalledPackage.new(
141+ "foo", "1.0", "/path/to/foo/1.0", False)
142+ self.foo_clone = Click.InstalledPackage.new(
143+ "foo", "1.0", "/path/to/foo/1.0", False)
144+ self.foo_different_version = Click.InstalledPackage.new(
145+ "foo", "2.0", "/path/to/foo/1.0", False)
146+ self.foo_different_path = Click.InstalledPackage.new(
147+ "foo", "1.0", "/path/to/foo/2.0", False)
148+ self.foo_different_writeable = Click.InstalledPackage.new(
149+ "foo", "1.0", "/path/to/foo/1.0", True)
150+ self.bar = Click.InstalledPackage.new(
151+ "bar", "1.0", "/path/to/foo/1.0", False)
152+
153+ def test_hash(self):
154+ self.assertIsInstance(self.foo.hash(), int)
155+ self.assertEqual(self.foo.hash(), self.foo_clone.hash())
156+ self.assertNotEqual(self.foo.hash(), self.foo_different_version.hash())
157+ self.assertNotEqual(self.foo.hash(), self.foo_different_path.hash())
158+ self.assertNotEqual(
159+ self.foo.hash(), self.foo_different_writeable.hash())
160+ self.assertNotEqual(self.foo.hash(), self.bar.hash())
161+
162+ # GLib doesn't allow passing an InstalledPackage as an argument here.
163+ @unittest.expectedFailure
164+ def test_equal_to(self):
165+ self.assertTrue(self.foo.equal_to(self.foo_clone))
166+ self.assertFalse(self.foo.equal_to(self.foo_different_version))
167+ self.assertFalse(self.foo.equal_to(self.foo_different_path))
168+ self.assertFalse(self.foo.equal_to(self.foo_different_writeable))
169+ self.assertFalse(self.foo.equal_to(self.bar))
170+
171+
172 class TestClickSingleDB(TestCase):
173 def setUp(self):
174 super(TestClickSingleDB, self).setUp()
175@@ -66,6 +103,11 @@
176 self.assertRaisesDatabaseError(
177 Click.DatabaseError.DOES_NOT_EXIST, self.db.get_path, "a", "1.1")
178
179+ def test_has_package_version(self):
180+ os.makedirs(os.path.join(self.temp_dir, "a", "1.0"))
181+ self.assertTrue(self.db.has_package_version("a", "1.0"))
182+ self.assertFalse(self.db.has_package_version("a", "1.1"))
183+
184 def test_packages_current(self):
185 os.makedirs(os.path.join(self.temp_dir, "a", "1.0"))
186 os.makedirs(os.path.join(self.temp_dir, "a", "1.1"))
187@@ -107,9 +149,13 @@
188 def test_manifest(self):
189 manifest_path = os.path.join(
190 self.temp_dir, "a", "1.0", ".click", "info", "a.manifest")
191- manifest_obj = {"name": "a", "version": "1.0", "hooks": {"a-app": {}}}
192+ manifest_obj = {
193+ "name": "a", "version": "1.0", "hooks": {"a-app": {}},
194+ "_should_be_removed": "",
195+ }
196 with mkfile(manifest_path) as manifest:
197 json.dump(manifest_obj, manifest)
198+ del manifest_obj["_should_be_removed"]
199 manifest_obj["_directory"] = os.path.join(self.temp_dir, "a", "1.0")
200 self.assertEqual(
201 manifest_obj,
202@@ -124,6 +170,26 @@
203 Click.DatabaseError.DOES_NOT_EXIST,
204 self.db.get_manifest_as_string, "a", "1.1")
205
206+ def test_manifest_bad(self):
207+ manifest_path = os.path.join(
208+ self.temp_dir, "a", "1.0", ".click", "info", "a.manifest")
209+ with mkfile(manifest_path) as manifest:
210+ print("{bad syntax", file=manifest)
211+ self.assertRaisesDatabaseError(
212+ Click.DatabaseError.BAD_MANIFEST, self.db.get_manifest, "a", "1.0")
213+ self.assertRaisesDatabaseError(
214+ Click.DatabaseError.BAD_MANIFEST,
215+ self.db.get_manifest_as_string, "a", "1.0")
216+ manifest_path = os.path.join(
217+ self.temp_dir, "a", "1.1", ".click", "info", "a.manifest")
218+ with mkfile(manifest_path) as manifest:
219+ print("[0]", file=manifest)
220+ self.assertRaisesDatabaseError(
221+ Click.DatabaseError.BAD_MANIFEST, self.db.get_manifest, "a", "1.1")
222+ self.assertRaisesDatabaseError(
223+ Click.DatabaseError.BAD_MANIFEST,
224+ self.db.get_manifest_as_string, "a", "1.1")
225+
226 def test_app_running(self):
227 with self.run_in_subprocess(
228 "click_find_on_path", "g_spawn_sync",
229@@ -193,6 +259,43 @@
230 self.g_spawn_sync_side_effect, {b"ubuntu-app-pid": 0})
231 self.assertFalse(self.db.any_app_running("a", "1.0"))
232
233+ def test_any_app_running_missing_app(self):
234+ with self.run_in_subprocess("click_find_on_path") as (enter, preloads):
235+ enter()
236+ preloads["click_find_on_path"].side_effect = (
237+ lambda command: command == b"ubuntu-app-pid")
238+ self.assertRaisesDatabaseError(
239+ Click.DatabaseError.DOES_NOT_EXIST,
240+ self.db.any_app_running, "a", "1.0")
241+
242+ def test_any_app_running_bad_manifest(self):
243+ with self.run_in_subprocess(
244+ "click_find_on_path", "g_spawn_sync",
245+ ) as (enter, preloads):
246+ enter()
247+ manifest_path = os.path.join(
248+ self.temp_dir, "a", "1.0", ".click", "info", "a.manifest")
249+ with mkfile(manifest_path) as manifest:
250+ print("{bad syntax", file=manifest)
251+ preloads["click_find_on_path"].side_effect = (
252+ lambda command: command == b"ubuntu-app-pid")
253+ self.assertFalse(self.db.any_app_running("a", "1.0"))
254+ self.assertFalse(preloads["g_spawn_sync"].called)
255+
256+ def test_any_app_running_no_hooks(self):
257+ with self.run_in_subprocess(
258+ "click_find_on_path", "g_spawn_sync",
259+ ) as (enter, preloads):
260+ enter()
261+ manifest_path = os.path.join(
262+ self.temp_dir, "a", "1.0", ".click", "info", "a.manifest")
263+ with mkfile(manifest_path) as manifest:
264+ json.dump({}, manifest)
265+ preloads["click_find_on_path"].side_effect = (
266+ lambda command: command == b"ubuntu-app-pid")
267+ self.assertFalse(self.db.any_app_running("a", "1.0"))
268+ self.assertFalse(preloads["g_spawn_sync"].called)
269+
270 def test_maybe_remove_registered(self):
271 with self.run_in_subprocess(
272 "click_find_on_path", "g_spawn_sync",
273@@ -262,9 +365,11 @@
274
275 def test_gc(self):
276 with self.run_in_subprocess(
277- "click_find_on_path", "g_spawn_sync",
278+ "click_find_on_path", "g_spawn_sync", "getpwnam"
279 ) as (enter, preloads):
280 enter()
281+ preloads["getpwnam"].side_effect = (
282+ lambda name: self.make_pointer(Passwd(pw_uid=1, pw_gid=1)))
283 os.environ["TEST_QUIET"] = "1"
284 a_path = os.path.join(self.temp_dir, "a", "1.0")
285 a_manifest_path = os.path.join(
286@@ -298,18 +403,24 @@
287 self.assertTrue(os.path.exists(c_path))
288
289 def test_gc_ignores_non_directory(self):
290- a_path = os.path.join(self.temp_dir, "a", "1.0")
291- a_manifest_path = os.path.join(
292- a_path, ".click", "info", "a.manifest")
293- with mkfile(a_manifest_path) as manifest:
294- json.dump({"hooks": {"a-app": {}}}, manifest)
295- a_user_path = os.path.join(
296- self.temp_dir, ".click", "users", "test-user", "a")
297- os.makedirs(os.path.dirname(a_user_path))
298- os.symlink(a_path, a_user_path)
299- touch(os.path.join(self.temp_dir, "file"))
300- self.db.gc()
301- self.assertTrue(os.path.exists(a_path))
302+ with self.run_in_subprocess(
303+ "getpwnam"
304+ ) as (enter, preloads):
305+ enter()
306+ preloads["getpwnam"].side_effect = (
307+ lambda name: self.make_pointer(Passwd(pw_uid=1, pw_gid=1)))
308+ a_path = os.path.join(self.temp_dir, "a", "1.0")
309+ a_manifest_path = os.path.join(
310+ a_path, ".click", "info", "a.manifest")
311+ with mkfile(a_manifest_path) as manifest:
312+ json.dump({"hooks": {"a-app": {}}}, manifest)
313+ a_user_path = os.path.join(
314+ self.temp_dir, ".click", "users", "test-user", "a")
315+ os.makedirs(os.path.dirname(a_user_path))
316+ os.symlink(a_path, a_user_path)
317+ touch(os.path.join(self.temp_dir, "file"))
318+ self.db.gc()
319+ self.assertTrue(os.path.exists(a_path))
320
321 def _make_ownership_test(self):
322 path = os.path.join(self.temp_dir, "a", "1.0")
323@@ -396,6 +507,38 @@
324 [(1, 1)],
325 set(args[0][1:] for args in preloads["chown"].call_args_list))
326
327+ def test_ensure_ownership_missing_clickpkg_user(self):
328+ with self.run_in_subprocess("getpwnam") as (enter, preloads):
329+ enter()
330+ preloads["getpwnam"].return_value = None
331+ self.assertRaisesDatabaseError(
332+ Click.DatabaseError.ENSURE_OWNERSHIP, self.db.ensure_ownership)
333+
334+ def test_ensure_ownership_failed_chown(self):
335+ def stat_side_effect(name, limit, ver, path, buf):
336+ st = self.convert_stat_pointer(name, buf)
337+ if path == limit:
338+ st.st_uid = 2
339+ st.st_gid = 2
340+ return 0
341+ else:
342+ self.delegate_to_original(name)
343+ return -1
344+
345+ with self.run_in_subprocess(
346+ "chown", "getpwnam", "__xstat", "__xstat64",
347+ ) as (enter, preloads):
348+ enter()
349+ preloads["chown"].return_value = -1
350+ preloads["getpwnam"].side_effect = (
351+ lambda name: self.make_pointer(Passwd(pw_uid=1, pw_gid=1)))
352+ self._set_stat_side_effect(
353+ preloads, stat_side_effect, self.db.props.root)
354+
355+ self._make_ownership_test()
356+ self.assertRaisesDatabaseError(
357+ Click.DatabaseError.ENSURE_OWNERSHIP, self.db.ensure_ownership)
358+
359
360 class TestClickDB(TestCase):
361 def setUp(self):
362@@ -429,6 +572,17 @@
363 db = Click.DB()
364 self.assertEqual(0, db.props.size)
365
366+ def test_read_nonexistent(self):
367+ db = Click.DB()
368+ db.read(db_dir=os.path.join(self.temp_dir, "nonexistent"))
369+ self.assertEqual(0, db.props.size)
370+
371+ def test_read_not_directory(self):
372+ path = os.path.join(self.temp_dir, "file")
373+ touch(path)
374+ db = Click.DB()
375+ self.assertRaisesFileError(GLib.FileError.NOTDIR, db.read, db_dir=path)
376+
377 def test_add(self):
378 db = Click.DB()
379 self.assertEqual(0, db.props.size)
380@@ -474,6 +628,24 @@
381 os.path.join(self.temp_dir, "b", "pkg", "1.1"),
382 db.get_path("pkg", "1.1"))
383
384+ def test_has_package_version(self):
385+ with open(os.path.join(self.temp_dir, "a.conf"), "w") as a:
386+ print("[Click Database]", file=a)
387+ print("root = %s" % os.path.join(self.temp_dir, "a"), file=a)
388+ with open(os.path.join(self.temp_dir, "b.conf"), "w") as b:
389+ print("[Click Database]", file=b)
390+ print("root = %s" % os.path.join(self.temp_dir, "b"), file=b)
391+ db = Click.DB()
392+ db.read(db_dir=self.temp_dir)
393+ self.assertFalse(db.has_package_version("pkg", "1.0"))
394+ os.makedirs(os.path.join(self.temp_dir, "a", "pkg", "1.0"))
395+ self.assertTrue(db.has_package_version("pkg", "1.0"))
396+ self.assertFalse(db.has_package_version("pkg", "1.1"))
397+ os.makedirs(os.path.join(self.temp_dir, "b", "pkg", "1.0"))
398+ self.assertTrue(db.has_package_version("pkg", "1.0"))
399+ os.makedirs(os.path.join(self.temp_dir, "b", "pkg", "1.1"))
400+ self.assertTrue(db.has_package_version("pkg", "1.1"))
401+
402 def test_packages_current(self):
403 with open(os.path.join(self.temp_dir, "a.conf"), "w") as a:
404 print("[Click Database]", file=a)
405@@ -570,6 +742,31 @@
406 b_manifest_obj,
407 json.loads(db.get_manifest_as_string("pkg", "1.1")))
408
409+ def test_manifest_bad(self):
410+ with open(os.path.join(self.temp_dir, "a.conf"), "w") as a:
411+ print("[Click Database]", file=a)
412+ print("root = %s" % os.path.join(self.temp_dir, "a"), file=a)
413+ db = Click.DB()
414+ db.read(db_dir=self.temp_dir)
415+ manifest_path = os.path.join(
416+ self.temp_dir, "a", "pkg", "1.0", ".click", "info", "pkg.manifest")
417+ with mkfile(manifest_path) as manifest:
418+ print("{bad syntax", file=manifest)
419+ self.assertRaisesDatabaseError(
420+ Click.DatabaseError.BAD_MANIFEST, db.get_manifest, "pkg", "1.0")
421+ self.assertRaisesDatabaseError(
422+ Click.DatabaseError.BAD_MANIFEST,
423+ db.get_manifest_as_string, "pkg", "1.0")
424+ manifest_path = os.path.join(
425+ self.temp_dir, "a", "pkg", "1.1", ".click", "info", "pkg.manifest")
426+ with mkfile(manifest_path) as manifest:
427+ print("[0]", file=manifest)
428+ self.assertRaisesDatabaseError(
429+ Click.DatabaseError.BAD_MANIFEST, db.get_manifest, "pkg", "1.0")
430+ self.assertRaisesDatabaseError(
431+ Click.DatabaseError.BAD_MANIFEST,
432+ db.get_manifest_as_string, "pkg", "1.0")
433+
434 def test_manifests_current(self):
435 with open(os.path.join(self.temp_dir, "a.conf"), "w") as a:
436 print("[Click Database]", file=a)
437
438=== modified file 'click/tests/test_framework.py'
439--- click/tests/test_framework.py 2014-06-23 21:14:06 +0000
440+++ click/tests/test_framework.py 2014-08-06 23:22:03 +0000
441@@ -27,7 +27,7 @@
442
443 from gi.repository import Click
444
445-from click.tests.helpers import TestCase
446+from click.tests.helpers import TestCase, touch
447
448
449 class TestClickFramework(TestCase):
450@@ -78,6 +78,48 @@
451 ["ubuntu-sdk-13.10", "ubuntu-sdk-14.04", "ubuntu-sdk-14.10"],
452 sorted(f.props.name for f in Click.Framework.get_frameworks()))
453
454+ def test_get_frameworks_nonexistent(self):
455+ with self.run_in_subprocess(
456+ "click_get_frameworks_dir") as (enter, preloads):
457+ enter()
458+ frameworks_dir = os.path.join(self.temp_dir, "nonexistent")
459+ preloads["click_get_frameworks_dir"].side_effect = (
460+ lambda: self.make_string(frameworks_dir))
461+ self.assertEqual([], Click.Framework.get_frameworks())
462+
463+ def test_get_frameworks_not_directory(self):
464+ with self.run_in_subprocess(
465+ "click_get_frameworks_dir") as (enter, preloads):
466+ enter()
467+ path = os.path.join(self.temp_dir, "file")
468+ touch(path)
469+ preloads["click_get_frameworks_dir"].side_effect = (
470+ lambda: self.make_string(path))
471+ self.assertEqual([], Click.Framework.get_frameworks())
472+
473+ def test_get_frameworks_ignores_other_files(self):
474+ with self.run_in_subprocess(
475+ "click_get_frameworks_dir") as (enter, preloads):
476+ enter()
477+ frameworks_dir = os.path.join(self.temp_dir, "frameworks")
478+ Click.ensuredir(frameworks_dir)
479+ touch(os.path.join(frameworks_dir, "file"))
480+ preloads["click_get_frameworks_dir"].side_effect = (
481+ lambda: self.make_string(frameworks_dir))
482+ self.assertEqual([], Click.Framework.get_frameworks())
483+
484+ def test_get_frameworks_ignores_unopenable_files(self):
485+ with self.run_in_subprocess(
486+ "click_get_frameworks_dir") as (enter, preloads):
487+ enter()
488+ frameworks_dir = os.path.join(self.temp_dir, "frameworks")
489+ Click.ensuredir(frameworks_dir)
490+ os.symlink(
491+ "nonexistent", os.path.join(frameworks_dir, "foo.framework"))
492+ preloads["click_get_frameworks_dir"].side_effect = (
493+ lambda: self.make_string(frameworks_dir))
494+ self.assertEqual([], Click.Framework.get_frameworks())
495+
496 def test_fields(self):
497 with self.run_in_subprocess(
498 "click_get_frameworks_dir") as (enter, preloads):
499
500=== modified file 'click/tests/test_hooks.py'
501--- click/tests/test_hooks.py 2014-06-23 21:14:06 +0000
502+++ click/tests/test_hooks.py 2014-08-06 23:22:03 +0000
503@@ -164,8 +164,19 @@
504 self.assertEqual(
505 "/usr/share/test/${id}.test", hook.get_field("pattern"))
506 self.assertEqual("test-update", hook.get_field("exec"))
507+ self.assertRaisesHooksError(
508+ Click.HooksError.MISSING_FIELD, hook.get_field, "nonexistent")
509 self.assertFalse(hook.props.is_user_level)
510
511+ def test_open_unopenable_file(self):
512+ with self.run_in_subprocess(
513+ "click_get_hooks_dir") as (enter, preloads):
514+ enter()
515+ self._setup_hooks_dir(preloads)
516+ os.symlink("nonexistent", os.path.join(self.hooks_dir, "foo.hook"))
517+ self.assertRaisesHooksError(
518+ Click.HooksError.NO_SUCH_HOOK, Click.Hook.open, self.db, "foo")
519+
520 def test_hook_name_absent(self):
521 with self.run_in_subprocess(
522 "click_get_hooks_dir") as (enter, preloads):
523@@ -205,6 +216,12 @@
524 self.assertRaisesHooksError(
525 Click.HooksError.BAD_APP_NAME, hook.get_app_id,
526 "package", "0.1", "app/name")
527+ self.assertRaisesHooksError(
528+ Click.HooksError.BAD_APP_NAME, hook.get_pattern,
529+ "package", "0.1", "app_name")
530+ self.assertRaisesHooksError(
531+ Click.HooksError.BAD_APP_NAME, hook.get_pattern,
532+ "package", "0.1", "app/name")
533
534 def test_short_id_invalid(self):
535 with self.run_in_subprocess(
536@@ -250,6 +267,21 @@
537 self.assertEqual(
538 [[b"/bin/sh", b"-c", b"test-update"]], self.spawn_calls)
539
540+ def test_run_commands_fail(self):
541+ with self.run_in_subprocess(
542+ "click_get_hooks_dir", "g_spawn_sync") as (enter, preloads):
543+ enter()
544+ self._setup_hooks_dir(preloads)
545+ preloads["g_spawn_sync"].side_effect = partial(
546+ self.g_spawn_sync_side_effect, {b"/bin/sh": 1})
547+ with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
548+ print("Exec: test-update", file=f)
549+ print("User: root", file=f)
550+ hook = Click.Hook.open(self.db, "test")
551+ self.assertRaisesHooksError(
552+ Click.HooksError.COMMAND_FAILED, hook.run_commands,
553+ user_name=None)
554+
555 def test_install_package(self):
556 with self.run_in_subprocess(
557 "click_get_hooks_dir") as (enter, preloads):
558@@ -481,6 +513,8 @@
559 "${home}/.local/share/test/${id}.test",
560 hook.get_field("pattern"))
561 self.assertEqual("test-update", hook.get_field("exec"))
562+ self.assertRaisesHooksError(
563+ Click.HooksError.MISSING_FIELD, hook.get_field, "nonexistent")
564 self.assertTrue(hook.props.is_user_level)
565
566 def test_hook_name_absent(self):
567@@ -523,6 +557,12 @@
568 self.assertRaisesHooksError(
569 Click.HooksError.BAD_APP_NAME, hook.get_app_id,
570 "package", "0.1", "app/name")
571+ self.assertRaisesHooksError(
572+ Click.HooksError.BAD_APP_NAME, hook.get_pattern,
573+ "package", "0.1", "app_name")
574+ self.assertRaisesHooksError(
575+ Click.HooksError.BAD_APP_NAME, hook.get_pattern,
576+ "package", "0.1", "app/name")
577
578 def test_short_id_valid(self):
579 with self.run_in_subprocess(
580@@ -559,6 +599,21 @@
581 self.assertEqual(
582 [[b"/bin/sh", b"-c", b"test-update"]], self.spawn_calls)
583
584+ def test_run_commands_fail(self):
585+ with self.run_in_subprocess(
586+ "click_get_hooks_dir", "g_spawn_sync") as (enter, preloads):
587+ enter()
588+ self._setup_hooks_dir(preloads)
589+ preloads["g_spawn_sync"].side_effect = partial(
590+ self.g_spawn_sync_side_effect, {b"/bin/sh": 1})
591+ with mkfile(os.path.join(self.temp_dir, "test.hook")) as f:
592+ print("User-Level: yes", file=f)
593+ print("Exec: test-update", file=f)
594+ hook = Click.Hook.open(self.db, "test")
595+ self.assertRaisesHooksError(
596+ Click.HooksError.COMMAND_FAILED, hook.run_commands,
597+ user_name=self.TEST_USER)
598+
599 def test_install_package(self):
600 with self.run_in_subprocess(
601 "click_get_hooks_dir", "click_get_user_home",
602@@ -693,12 +748,14 @@
603
604 def test_install(self):
605 with self.run_in_subprocess(
606- "click_get_hooks_dir", "click_get_user_home",
607+ "click_get_hooks_dir", "click_get_user_home", "getpwnam"
608 ) as (enter, preloads):
609 enter()
610 # Don't tell click about the hooks directory yet.
611 self._setup_hooks_dir(preloads)
612 preloads["click_get_user_home"].return_value = "/home/test-user"
613+ preloads["getpwnam"].side_effect = (
614+ lambda name: self.make_pointer(Passwd(pw_uid=1, pw_gid=1)))
615 with mkfile(os.path.join(self.temp_dir, "hooks", "new.hook")) as f:
616 print("User-Level: yes", file=f)
617 print("Pattern: %s/${id}.new" % self.temp_dir, file=f)
618
619=== modified file 'click/tests/test_osextras.py'
620--- click/tests/test_osextras.py 2014-03-03 21:26:40 +0000
621+++ click/tests/test_osextras.py 2014-08-06 23:22:03 +0000
622@@ -113,6 +113,11 @@
623 self.use_temp_dir()
624 self.mod = Click
625
626+ def test_ensuredir_error(self):
627+ path = os.path.join(self.temp_dir, "file")
628+ touch(path)
629+ self.assertRaisesFileError(mock.ANY, self.mod.ensuredir, path)
630+
631 def test_dir_read_name_directory_present(self):
632 new_dir = os.path.join(self.temp_dir, "dir")
633 touch(os.path.join(new_dir, "file"))
634@@ -136,6 +141,17 @@
635 os.mkdir(path)
636 self.assertRaisesFileError(mock.ANY, self.mod.unlink_force, path)
637
638+ def test_symlink_unlink_error(self):
639+ path = os.path.join(self.temp_dir, "dir")
640+ os.mkdir(path)
641+ self.assertRaisesFileError(
642+ mock.ANY, self.mod.symlink_force, "source", path)
643+
644+ def test_symlink_error(self):
645+ path = os.path.join(self.temp_dir, "dir", "file")
646+ self.assertRaisesFileError(
647+ mock.ANY, self.mod.symlink_force, "source", path)
648+
649
650 class TestOSExtrasPython(TestCase, TestOSExtrasBaseMixin):
651 def setUp(self):
652@@ -143,6 +159,11 @@
653 self.use_temp_dir()
654 self.mod = osextras
655
656+ def test_ensuredir_oserror(self):
657+ path = os.path.join(self.temp_dir, "file")
658+ touch(path)
659+ self.assertRaises(OSError, self.mod.ensuredir, path)
660+
661 def test_listdir_directory_present(self):
662 new_dir = os.path.join(self.temp_dir, "dir")
663 touch(os.path.join(new_dir, "file"))
664@@ -161,3 +182,12 @@
665 path = os.path.join(self.temp_dir, "dir")
666 os.mkdir(path)
667 self.assertRaises(OSError, self.mod.unlink_force, path)
668+
669+ def test_symlink_unlink_oserror(self):
670+ path = os.path.join(self.temp_dir, "dir")
671+ os.mkdir(path)
672+ self.assertRaises(OSError, self.mod.symlink_force, "source", path)
673+
674+ def test_symlink_oserror(self):
675+ path = os.path.join(self.temp_dir, "dir", "file")
676+ self.assertRaises(OSError, self.mod.symlink_force, "source", path)
677
678=== added file 'click/tests/test_paths.py.in'
679--- click/tests/test_paths.py.in 1970-01-01 00:00:00 +0000
680+++ click/tests/test_paths.py.in 2014-08-06 23:22:03 +0000
681@@ -0,0 +1,42 @@
682+# Copyright (C) 2013 Canonical Ltd.
683+# Author: Colin Watson <cjwatson@ubuntu.com>
684+
685+# This program is free software: you can redistribute it and/or modify
686+# it under the terms of the GNU General Public License as published by
687+# the Free Software Foundation; version 3 of the License.
688+#
689+# This program is distributed in the hope that it will be useful,
690+# but WITHOUT ANY WARRANTY; without even the implied warranty of
691+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
692+# GNU General Public License for more details.
693+#
694+# You should have received a copy of the GNU General Public License
695+# along with this program. If not, see <http://www.gnu.org/licenses/>.
696+
697+"""Unit tests for click.paths.
698+
699+This is mostly just to reduce noise in the coverage report.
700+"""
701+
702+from __future__ import print_function
703+
704+__metaclass__ = type
705+__all__ = [
706+ 'TestClickPaths',
707+ ]
708+
709+
710+from gi.repository import Click
711+
712+from click.tests.helpers import TestCase
713+
714+
715+class TestClickPaths(TestCase):
716+ def test_get_hooks_dir(self):
717+ self.assertEqual("@pkgdatadir@/hooks", Click.get_hooks_dir())
718+
719+ def test_get_db_dir(self):
720+ self.assertEqual("@sysconfdir@/click/databases", Click.get_db_dir())
721+
722+ def test_get_frameworks_dir(self):
723+ self.assertEqual("@pkgdatadir@/frameworks", Click.get_frameworks_dir())
724
725=== modified file 'click/tests/test_user.py'
726--- click/tests/test_user.py 2014-05-30 09:09:20 +0000
727+++ click/tests/test_user.py 2014-08-06 23:22:03 +0000
728@@ -27,11 +27,11 @@
729 import os
730 import shutil
731
732-from gi.repository import Click
733+from gi.repository import Click, GLib
734
735 from click.json_helpers import json_array_to_python, json_object_to_python
736 from click.tests.gimock_types import Passwd
737-from click.tests.helpers import TestCase, mkfile
738+from click.tests.helpers import TestCase, mkfile, touch
739
740
741 class TestClickUser(TestCase):
742@@ -92,6 +92,17 @@
743 os.path.join(db_root, ".click", "users", "test-user"),
744 registry.get_overlay_db())
745
746+ def test_new_db_not_directory(self):
747+ with self.run_in_subprocess(
748+ "click_get_db_dir", "g_get_user_name") as (enter, preloads):
749+ enter()
750+ path = os.path.join(self.temp_dir, "file")
751+ touch(path)
752+ preloads["click_get_db_dir"].side_effect = (
753+ lambda: self.make_string(path))
754+ self.assertRaisesFileError(
755+ GLib.FileError.NOTDIR, Click.User.for_user, None, None)
756+
757 def test_get_overlay_db(self):
758 self.assertEqual(
759 os.path.join(self.temp_dir, ".click", "users", "user"),
760@@ -141,6 +152,46 @@
761 preloads["chown"].assert_any_call(
762 os.path.join(click_dir, "users", "user").encode(), 2, 2)
763
764+ def test_ensure_db_mkdir_fails(self):
765+ with self.run_in_subprocess("mkdir") as (enter, preloads):
766+ enter()
767+ preloads["mkdir"].return_value = -1
768+ registry = Click.User.for_user(self.db, "user")
769+ self.assertRaisesUserError(
770+ Click.UserError.CREATE_DB, registry.set_version, "a", "1.0")
771+
772+ def test_ensure_db_chown_fails(self):
773+ with self.run_in_subprocess(
774+ "chown", "geteuid", "getpwnam") as (enter, preloads):
775+ enter()
776+ preloads["geteuid"].return_value = 0
777+ getpwnam_result = Passwd()
778+
779+ def getpwnam_side_effect(name):
780+ if name == b"clickpkg":
781+ getpwnam_result.pw_uid = 1
782+ getpwnam_result.pw_gid = 1
783+ else:
784+ getpwnam_result.pw_uid = 2
785+ getpwnam_result.pw_gid = 2
786+ return self.make_pointer(getpwnam_result)
787+
788+ preloads["getpwnam"].side_effect = getpwnam_side_effect
789+ preloads["chown"].return_value = -1
790+ registry = Click.User.for_user(self.db, "user")
791+ self.assertRaisesUserError(
792+ Click.UserError.CHOWN_DB, registry.set_version, "a", "1.0")
793+
794+ def test_ensure_db_getpwnam_fails(self):
795+ with self.run_in_subprocess(
796+ "geteuid", "getpwnam") as (enter, preloads):
797+ enter()
798+ preloads["geteuid"].return_value = 0
799+ preloads["getpwnam"].return_value = None
800+ registry = Click.User.for_user(self.db, "user")
801+ self.assertRaisesUserError(
802+ Click.UserError.GETPWNAM, registry.set_version, "a", "1.0")
803+
804 def test_get_package_names_missing(self):
805 db = Click.DB()
806 db.add(os.path.join(self.temp_dir, "nonexistent"))
807
808=== modified file 'debian/changelog'
809--- debian/changelog 2014-07-04 15:10:01 +0000
810+++ debian/changelog 2014-08-06 23:22:03 +0000
811@@ -1,3 +1,18 @@
812+click (0.4.30) UNRELEASED; urgency=medium
813+
814+ [ Colin Watson ]
815+ * Add many more unit tests to fill in some gaps in the coverage report.
816+
817+ [ Michael Vogt ]
818+ * Exclude non-existing users from User.get_user_names()
819+ (LP: #1334611)
820+
821+ [ Zoltan Balogh ]
822+ * Add a set of APIs to the 14.10 frameworks. Add ubuntu-ui-toolkit-doc to
823+ all frameworks.
824+
825+ -- Michael Vogt <michael.vogt@ubuntu.com> Fri, 11 Jul 2014 19:17:47 +0200
826+
827 click (0.4.29.1) utopic; urgency=medium
828
829 [ Michael Vogt ]
830
831=== modified file 'lib/click/user.vala'
832--- lib/click/user.vala 2014-05-30 09:09:20 +0000
833+++ lib/click/user.vala 2014-08-06 23:22:03 +0000
834@@ -195,6 +195,10 @@
835 foreach (var entry in Click.Dir.open (users_db)) {
836 if (entry in seen)
837 continue;
838+ // the user is not a pseudo user and does not/no-longer exist
839+ if (!entry.has_prefix ("@") &&
840+ Posix.getpwnam (entry) == null)
841+ continue;
842 var path = Path.build_filename (users_db,
843 entry);
844 if (is_dir (path)) {
845
846=== modified file 'tests/integration/helpers.py'
847--- tests/integration/helpers.py 2014-07-03 12:55:50 +0000
848+++ tests/integration/helpers.py 2014-08-06 23:22:03 +0000
849@@ -22,7 +22,6 @@
850 import random
851 import shutil
852 import string
853-import shutil
854 import subprocess
855 import tempfile
856 import unittest
857@@ -31,6 +30,7 @@
858 def is_root():
859 return os.getuid() == 0
860
861+
862 def has_network():
863 return subprocess.call(
864 ["ping", "-c1", "archive.ubuntu.com"]) == 0
865@@ -55,6 +55,7 @@
866 def setUp(self):
867 super(ClickTestCase, self).setUp()
868 self.temp_dir = tempfile.mkdtemp()
869+
870 def tearDown(self):
871 super(ClickTestCase, self).tearDown()
872 # we force the cleanup before removing the tempdir so that stuff
873@@ -64,13 +65,14 @@
874
875 def _create_manifest(self, target, name, version, framework, hooks={}):
876 with open(target, "w") as f:
877- json.dump({'name': name,
878- 'version': str(version),
879- 'maintainer': 'Foo Bar <foo@example.org>',
880- 'title': 'test title',
881- 'framework': framework,
882- 'hooks': hooks,
883- }, f)
884+ json.dump({
885+ 'name': name,
886+ 'version': str(version),
887+ 'maintainer': 'Foo Bar <foo@example.org>',
888+ 'title': 'test title',
889+ 'framework': framework,
890+ 'hooks': hooks,
891+ }, f)
892
893 def _make_click(self, name=None, version=1.0,
894 framework="ubuntu-sdk-13.10", hooks={}):
895
896=== modified file 'tests/integration/test_build_core_apps.py'
897--- tests/integration/test_build_core_apps.py 2014-07-02 18:21:32 +0000
898+++ tests/integration/test_build_core_apps.py 2014-08-06 23:22:03 +0000
899@@ -36,7 +36,7 @@
900 ]
901
902 # command to "configure"
903-CORE_APP_CONFIGURE_CMD = [
904+CORE_APP_CONFIGURE_CMD = [
905 "cmake", "..", "-DCLICK_MODE=on", "-DINSTALL_TESTS=off"]
906
907 # command to make install
908@@ -103,9 +103,9 @@
909 branch_dir = branch[len("lp:"):]
910 build_dir = os.path.join(branch_dir, "build-tree")
911 if os.path.exists(branch_dir):
912- subprocess.check_call(["bzr","pull"], cwd=branch_dir)
913+ subprocess.check_call(["bzr", "pull"], cwd=branch_dir)
914 else:
915- subprocess.check_call(["bzr","branch", branch])
916+ subprocess.check_call(["bzr", "branch", branch])
917 manifest = find_manifest(branch_dir)
918 # build it
919 self._set_arch_and_framework_from_manifest(manifest)
920@@ -117,6 +117,3 @@
921 self.configure()
922 self.make()
923 self.create_click()
924-
925-
926-
927
928=== modified file 'tests/integration/test_chroot.py'
929--- tests/integration/test_chroot.py 2014-07-02 17:16:36 +0000
930+++ tests/integration/test_chroot.py 2014-08-06 23:22:03 +0000
931@@ -24,6 +24,7 @@
932 ClickTestCase,
933 )
934
935+
936 @unittest.skipIf(not is_root(), "This tests needs to run as root")
937 @unittest.skipIf(not has_network(), "Need network")
938 class TestChroot(ClickTestCase):
939
940=== modified file 'tests/integration/test_hook.py'
941--- tests/integration/test_hook.py 2014-07-04 14:35:59 +0000
942+++ tests/integration/test_hook.py 2014-08-06 23:22:03 +0000
943@@ -55,20 +55,20 @@
944 subprocess.check_call(
945 [self.click_binary, "hook", "install", hook_name])
946 self.addCleanup(
947- subprocess.check_call, [self.click_binary, "hook", "remove",
948+ subprocess.check_call, [self.click_binary, "hook", "remove",
949 hook_name])
950 # make click that uses the hook
951- hooks = {'app1' : {hook_name: 'README'}}
952+ hooks = {'app1': {hook_name: 'README'}}
953 click_pkg_name = "com.example.hook-1"
954 click_pkg = self._make_click(
955 click_pkg_name, framework="", hooks=hooks)
956 user = os.environ.get("USER", "root")
957- subprocess.check_call([
958- self.click_binary, "install", "--user=%s" % user, click_pkg],
959- universal_newlines=True)
960+ subprocess.check_call(
961+ [self.click_binary, "install", "--user=%s" % user, click_pkg],
962+ universal_newlines=True)
963 self.addCleanup(
964- subprocess.check_call,
965- [self.click_binary, "unregister","--user=%s" % user,
966+ subprocess.check_call,
967+ [self.click_binary, "unregister", "--user=%s" % user,
968 click_pkg_name])
969 # ensure we have the hook
970 generated_hook_file = os.path.expanduser(

Subscribers

People subscribed via source and target branches

to all changes: