Merge lp:~jml/libdep-service/copy-in-database into lp:libdep-service

Proposed by James Westby
Status: Merged
Approved by: James Westby
Approved revision: 82
Merged at revision: 81
Proposed branch: lp:~jml/libdep-service/copy-in-database
Merge into: lp:libdep-service
Diff against target: 555 lines (+535/-0)
3 files modified
djlibdep/tests/__init__.py (+2/-0)
djlibdep/tests/test_configuration.py (+50/-0)
djlibdep/tests/test_database.py (+483/-0)
To merge this branch: bzr merge lp:~jml/libdep-service/copy-in-database
Reviewer Review Type Date Requested Status
James Westby (community) Approve
Review via email: mp+133966@code.launchpad.net

Commit message

Add the tests from pkgme-devportal.

To post a comment you must log in.
Revision history for this message
James Westby (james-w) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'djlibdep/tests/__init__.py'
2--- djlibdep/tests/__init__.py 2012-10-29 17:35:05 +0000
3+++ djlibdep/tests/__init__.py 2012-11-12 17:52:24 +0000
4@@ -21,6 +21,8 @@
5 TEST_MODULES = [
6 'api',
7 'aptfile',
8+ 'configuration',
9+ 'database',
10 'interface',
11 'pep8',
12 'preflight',
13
14=== added file 'djlibdep/tests/test_configuration.py'
15--- djlibdep/tests/test_configuration.py 1970-01-01 00:00:00 +0000
16+++ djlibdep/tests/test_configuration.py 2012-11-12 17:52:24 +0000
17@@ -0,0 +1,50 @@
18+# Copyright 2011-2012 Canonical Ltd. This software is licensed under the
19+# GNU Affero General Public License version 3 (see the file LICENSE).
20+
21+import os
22+
23+from fixtures import (
24+ EnvironmentVariableFixture,
25+ TempDir,
26+ )
27+from testtools import TestCase
28+
29+from devportalbinary.configuration import (
30+ _DEFAULT_CONF_FILE,
31+ CONF_FILE_ENV_VAR,
32+ get_config_file_path,
33+ load_configuration,
34+ )
35+from devportalbinary.testing import make_config_section
36+
37+
38+class TestConfigFileLocation(TestCase):
39+
40+ def test_default(self):
41+ self.assertEqual(
42+ os.path.expanduser(_DEFAULT_CONF_FILE), get_config_file_path())
43+
44+ def test_env_var_override(self):
45+ tempdir = self.useFixture(TempDir())
46+ config_file = os.path.join(tempdir.path, 'whatever.ini')
47+ self.useFixture(
48+ EnvironmentVariableFixture(CONF_FILE_ENV_VAR, config_file))
49+ self.assertEqual(config_file, get_config_file_path())
50+
51+ def test_load_configuration(self):
52+ # load_configuration reads from the configuration file returned by
53+ # get_config_file_path.
54+ tempdir = self.useFixture(TempDir())
55+ config_file = os.path.join(tempdir.path, 'whatever.ini')
56+ self.useFixture(
57+ EnvironmentVariableFixture(CONF_FILE_ENV_VAR, config_file))
58+ db_name = self.getUniqueString()
59+ with open(config_file, 'w') as f:
60+ f.write(make_config_section(
61+ 'database',
62+ {'db_type': 'postgres', 'db_name': db_name}))
63+ f.write('\n')
64+ config = load_configuration()
65+ self.assertEqual(
66+ ('postgres', db_name),
67+ (config.database_db_type, config.database_db_name))
68
69=== added file 'djlibdep/tests/test_database.py'
70--- djlibdep/tests/test_database.py 1970-01-01 00:00:00 +0000
71+++ djlibdep/tests/test_database.py 2012-11-12 17:52:24 +0000
72@@ -0,0 +1,483 @@
73+from collections import namedtuple
74+import os
75+
76+from fixtures import TempDir
77+from storm.databases.postgres import psycopg2
78+from storm.exceptions import ClosedError
79+from testresources import ResourcedTestCase
80+from testtools import TestCase
81+from testtools.matchers import (
82+ Equals,
83+ Matcher,
84+)
85+from treeshape import (
86+ CONTENT,
87+ FileTree,
88+)
89+
90+from devportalbinary.database import (
91+ AptFilePackageDatabase,
92+ deb_file_url_for_publication,
93+ dict_add,
94+ find_file_under_dir,
95+ get_dependency_database,
96+ get_file_contents,
97+ get_package_info_from_publication,
98+ is_library_package,
99+ LibdepServiceClient,
100+ libdep_mapping_from_symbols,
101+ load_configuration,
102+ NoSharedObject,
103+ possible_sonames_for_shared_object,
104+ PackageDatabase,
105+ publishings_to_package_info,
106+ shared_object_info_to_soname,
107+ shared_objects_from_shlibs,
108+ TooManySharedObjects,
109+)
110+from devportalbinary.testing import (
111+ ConfigFileFixture,
112+ ConfigSettings,
113+ postgres_db_resource,
114+)
115+
116+from djlibdep.test_double import LibdepServiceDouble
117+from libdep_service_client.client import Client
118+
119+
120+class ResultsIn(Matcher):
121+
122+ def __init__(self, db, rows):
123+ self._db = db
124+ self._rows = rows
125+
126+ def match(self, query):
127+ # XXX: Abstraction violation
128+ results = self._db._store.execute(query)
129+ return Equals(self._rows).match(list(results))
130+
131+
132+class TestDatabase(TestCase, ResourcedTestCase):
133+
134+ resources = [
135+ ('db_fixture', postgres_db_resource),
136+ ]
137+
138+ def get_package_db(self):
139+ db = PackageDatabase(self.db_fixture.conn)
140+ self.addCleanup(db.close)
141+ return db
142+
143+ def test_insert_new_library(self):
144+ db = self.get_package_db()
145+ db.insert_new_library('foo-src', 'libfoo.so.0', 'foo', 'i386')
146+ self.assertThat(
147+ "SELECT source_package_name, library, dependency, architecture "
148+ "FROM libdep",
149+ ResultsIn(db, [('foo-src', 'libfoo.so.0', 'foo', 'i386')]))
150+
151+ def test_double_insert(self):
152+ db = self.get_package_db()
153+ db.insert_new_library('foo-src', 'libfoo.so.0', 'foo', 'i386')
154+ self.assertRaises(
155+ psycopg2.IntegrityError,
156+ db.insert_new_library, 'foo-src', 'libfoo.so.0', 'foo', 'i386')
157+
158+ def test_differing_dependencies(self):
159+ db = self.get_package_db()
160+ db.insert_new_library('foo-src', 'libfoo.so.0', 'foo', 'i386')
161+ db.insert_new_library('foo-src', 'libfoo.so.0', 'bar', 'i386')
162+ deps = db.get_multiple_dependencies(['libfoo.so.0'], 'i386')
163+ self.assertEqual(deps, {'libfoo.so.0': set(['foo', 'bar'])})
164+
165+ def test_get_dependencies(self):
166+ db = self.get_package_db()
167+ db.insert_new_library('foo-src', 'libfoo.so.0', 'foo', 'i386')
168+ deps = db.get_multiple_dependencies(['libfoo.so.0'], 'i386')
169+ self.assertEqual(deps, {'libfoo.so.0': set(['foo'])})
170+
171+ def test_respects_architecture(self):
172+ db = self.get_package_db()
173+ db.insert_new_library('foo-src', 'libfoo.so.0', 'foo', 'i386')
174+ db.insert_new_library('foo-src', 'libfoo.so.0', 'foo-amd64', 'amd64')
175+ deps = db.get_multiple_dependencies(['libfoo.so.0'], arch='amd64')
176+ self.assertEqual(deps, {'libfoo.so.0': set(['foo-amd64'])})
177+
178+ def test_unknown_library(self):
179+ db = self.get_package_db()
180+ deps = db.get_multiple_dependencies(['libfoo.so.0'], 'i386')
181+ self.assertEqual(deps, {})
182+
183+ def test_update_package(self):
184+ db = self.get_package_db()
185+ db.update_package(
186+ 'foo', {'i386': {'libfoo.so.1': 'foo-bin'}})
187+ deps = db.get_multiple_dependencies(['libfoo.so.1'], 'i386')
188+ self.assertEqual(deps, {'libfoo.so.1': set(['foo-bin'])})
189+
190+ def test_update_existing_package_no_libraries(self):
191+ db = self.get_package_db()
192+ db.update_package('foo', {'i386': {'libfoo.so.1': 'foo-bin'}})
193+ # Run again, this time with no symbols, representing that a newer
194+ # version of the package no longer exports any libraries.
195+ db.update_package('foo', {'i386': {}})
196+ deps = db.get_multiple_dependencies(['libfoo.so.1'], 'i386')
197+ self.assertEqual(deps, {})
198+
199+ def test_update_package_two_architectures(self):
200+ # If two architectures are updated separately then they
201+ # shouldn't interfere
202+ db = self.get_package_db()
203+ db.update_package('foo', {'i386': {'libfoo.so.1': 'foo-bin'}})
204+ db.update_package('foo', {'amd64': {'libfoo.so.1': 'foo-bin-amd64'}})
205+ deps = db.get_multiple_dependencies(['libfoo.so.1'], arch='i386')
206+ self.assertEqual(deps, {'libfoo.so.1': set(['foo-bin'])})
207+
208+ def test_close(self):
209+ # Test that we can close the package db.
210+ db = PackageDatabase(self.db_fixture.conn)
211+ db.close()
212+ self.assertRaises(
213+ ClosedError, db.insert_new_library, 'foo',
214+ 'libfoo.so.1', 'foo-bin', 'i386')
215+
216+ def test_close_twice(self):
217+ # Test that we can close the package db twice with no exception.
218+ db = PackageDatabase(self.db_fixture.conn)
219+ db.close()
220+ db.close()
221+
222+
223+class TestDatabaseConfiguration(TestCase):
224+
225+ def use_database_config(self, **db_settings):
226+ return self.useFixture(ConfigSettings(('database', db_settings)))
227+
228+ def test_get_db_info_from_config_sqlite(self):
229+ other_tempdir = self.useFixture(TempDir())
230+ expected_db_path = os.path.join(other_tempdir.path, 'db')
231+ self.use_database_config(db_type='sqlite', path=expected_db_path)
232+ options = load_configuration()
233+ self.assertRaises(
234+ ValueError, PackageDatabase.get_db_info_from_config, options)
235+
236+ def test_default_create_no_config(self):
237+ nonexistent = self.getUniqueString()
238+ self.useFixture(ConfigFileFixture(nonexistent))
239+ self.assertIsInstance(
240+ get_dependency_database(), AptFilePackageDatabase)
241+
242+ def test_default_create_empty_config(self):
243+ self.useFixture(ConfigSettings())
244+ self.assertIsInstance(
245+ get_dependency_database(), AptFilePackageDatabase)
246+
247+ def test_remote_service(self):
248+ base_url = 'http://example.com/libdep-service/'
249+ self.use_database_config(db_type='libdep-service', base_url=base_url)
250+ db = get_dependency_database()
251+ self.assertIsInstance(db, LibdepServiceClient)
252+ self.assertEqual(base_url, db._client.base_url)
253+
254+ def test_get_db_info_from_config_postgres(self):
255+ expected_username = self.getUniqueString()
256+ expected_password = self.getUniqueString()
257+ expected_host = self.getUniqueString()
258+ expected_port = self.getUniqueInteger()
259+ expected_db_name = self.getUniqueString()
260+
261+ self.use_database_config(
262+ db_type='postgres',
263+ username=expected_username,
264+ password=expected_password,
265+ host=expected_host,
266+ port=expected_port,
267+ db_name=expected_db_name)
268+ options = load_configuration()
269+ uri = PackageDatabase.get_db_info_from_config(options)
270+ self.assertEqual(expected_db_name, uri.database)
271+ self.assertEqual(expected_port, uri.port)
272+ self.assertEqual(expected_host, uri.host)
273+ self.assertEqual(expected_password, uri.password)
274+ self.assertEqual(expected_username, uri.username)
275+
276+
277+class FakeBPPH(object):
278+
279+ def __init__(self):
280+ self.archive = namedtuple(
281+ 'Archive', 'web_link')('http://lp.net/archive')
282+ self.distro_arch_series = namedtuple(
283+ 'DistroArchSeries', 'architecture_tag')('i386')
284+ self.binary_package_name = 'foo'
285+ self.binary_package_version = '1'
286+ self.architecture_specific = True
287+
288+
289+class TestDebFileUrlForPublication(TestCase):
290+
291+ def test_get_url(self):
292+ bpph = FakeBPPH()
293+ expected_url = '%s/+files/%s_%s_%s.deb' % (
294+ bpph.archive.web_link,
295+ bpph.binary_package_name,
296+ bpph.binary_package_version,
297+ bpph.distro_arch_series.architecture_tag,
298+ )
299+ self.assertEqual(expected_url, deb_file_url_for_publication(bpph))
300+
301+ def test_get_url_with_epoch(self):
302+ # epochs are stripped from the version number
303+ bpph = FakeBPPH()
304+ bpph.binary_package_version = '1:1'
305+ expected_url = '%s/+files/%s_%s_%s.deb' % (
306+ bpph.archive.web_link,
307+ bpph.binary_package_name,
308+ '1',
309+ bpph.distro_arch_series.architecture_tag,
310+ )
311+ self.assertEqual(expected_url, deb_file_url_for_publication(bpph))
312+
313+ def test_get_url_for_arch_indep(self):
314+ # epochs are stripped from the version number
315+ bpph = FakeBPPH()
316+ bpph.architecture_specific = False
317+ expected_url = '%s/+files/%s_%s_all.deb' % (
318+ bpph.archive.web_link,
319+ bpph.binary_package_name,
320+ '1',
321+ )
322+ self.assertEqual(expected_url, deb_file_url_for_publication(bpph))
323+
324+
325+class TestShlibs(TestCase):
326+
327+ def test_empty(self):
328+ self.assertEqual({}, shared_objects_from_shlibs(""))
329+
330+ def test_comments(self):
331+ self.assertEqual({}, shared_objects_from_shlibs("# aaaaa\n"))
332+
333+ def test_blank_line(self):
334+ self.assertEqual({}, shared_objects_from_shlibs("\n"))
335+
336+ def test_whitespace_line(self):
337+ self.assertEqual({}, shared_objects_from_shlibs(" \n"))
338+
339+ def test_udeb_skipped(self):
340+ self.assertEqual(
341+ {}, shared_objects_from_shlibs("udeb: libfoo 1 libfoo\n"))
342+
343+ def test_simple_soname(self):
344+ self.assertEqual(
345+ {('libfoo', '1'): 'libfoo'},
346+ shared_objects_from_shlibs("libfoo 1 libfoo\n"))
347+
348+ def test_other_type_of_soname(self):
349+ self.assertEqual(
350+ {('libfoo', '4.8'): 'libfoo'},
351+ shared_objects_from_shlibs("libfoo 4.8 libfoo\n"))
352+
353+
354+class TestPossibleSonamesForSharedObject(TestCase):
355+
356+ def test_no_dot(self):
357+ self.assertEqual(
358+ set(['libfoo.so.1', 'libfoo-1.so']),
359+ possible_sonames_for_shared_object('libfoo', '1'))
360+
361+ def test_dot(self):
362+ self.assertEqual(
363+ set(['libfoo-1.0.so']),
364+ possible_sonames_for_shared_object('libfoo', '1.0'))
365+
366+
367+class TestFindFileUnderDir(TestCase):
368+
369+ def test_file_missing(self):
370+ tree = {}
371+ path = self.useFixture(FileTree(tree)).path
372+ self.assertEqual(None, find_file_under_dir('nothere', path))
373+
374+ def test_file_in_basedir(self):
375+ filename = 'here'
376+ tree = {filename: {}}
377+ built_tree = self.useFixture(FileTree(tree))
378+ self.assertEqual(
379+ built_tree.join(filename),
380+ find_file_under_dir(filename, built_tree.path))
381+
382+ def test_file_in_subdir(self):
383+ filename = 'here'
384+ relpath = 'there/' + filename
385+ tree = {relpath: {}}
386+ built_tree = self.useFixture(FileTree(tree))
387+ self.assertEqual(
388+ built_tree.join(relpath),
389+ find_file_under_dir(filename, built_tree.path))
390+
391+ def test_handles_multiple_matches(self):
392+ filename = 'here'
393+ relpath = 'there/' + filename
394+ tree = {filename: {}, relpath: {}}
395+ built_tree = self.useFixture(FileTree(tree))
396+ self.assertEqual(
397+ built_tree.join(filename),
398+ find_file_under_dir(filename, built_tree.path))
399+
400+
401+class TestSharedObjectInfoToSoname(TestCase):
402+
403+ def test_no_files(self):
404+ tree = {}
405+ path = self.useFixture(FileTree(tree)).path
406+ self.assertRaises(
407+ NoSharedObject,
408+ shared_object_info_to_soname, 'libfoo', '1', path)
409+
410+ def test_too_many_files(self):
411+ libname = 'libfoo'
412+ version = '1'
413+ possible_sonames = possible_sonames_for_shared_object(libname, version)
414+ tree = dict((name, {}) for name in possible_sonames)
415+ path = self.useFixture(FileTree(tree)).path
416+ self.assertRaises(
417+ TooManySharedObjects,
418+ shared_object_info_to_soname, libname, version, path)
419+
420+ def test_file_found(self):
421+ libname = 'libfoo'
422+ version = '1'
423+ expected_soname = '{0}.so.{1}'.format(libname, version)
424+ tree = {expected_soname: {}}
425+ path = self.useFixture(FileTree(tree)).path
426+ self.assertEqual(
427+ expected_soname,
428+ shared_object_info_to_soname(libname, version, path))
429+
430+
431+class TestDictAdd(TestCase):
432+
433+ def test_no_dicts(self):
434+ self.assertEqual({}, dict_add())
435+
436+ def test_one_dict(self):
437+ self.assertEqual(dict(a=1), dict_add(dict(a=1)))
438+
439+ def test_two_dicts(self):
440+ self.assertEqual(dict(a=1, b=2), dict_add(dict(a=1), dict(b=2)))
441+
442+ def test_precedence(self):
443+ self.assertEqual(dict(a=2), dict_add(dict(a=1), dict(a=2)))
444+
445+
446+class TestIsLibraryPackage(TestCase):
447+
448+ def test_lib_filename(self):
449+ self.assertEqual(
450+ True, is_library_package('http://launchpad.net/libfoo.deb'))
451+
452+ def test_whitelisted(self):
453+ self.assertEqual(
454+ True, is_library_package('http://launchpad.net/zlib1g.deb'))
455+
456+ def test_other(self):
457+ self.assertEqual(
458+ False, is_library_package('http://launchpad.net/bzr.deb'))
459+
460+
461+class TestGetPackageInfoFromPublication(TestCase):
462+
463+ def test_returns_url_and_arch(self):
464+ bpph = FakeBPPH()
465+ self.assertEqual(
466+ (deb_file_url_for_publication(bpph),
467+ bpph.distro_arch_series.architecture_tag),
468+ get_package_info_from_publication(bpph))
469+
470+
471+class TestPublishingsToPackageInfo(TestCase):
472+
473+ def test_integration(self):
474+ # This function is split out as it is the testable part of
475+ # fetch symbols files. This tests that the functions glue
476+ # together
477+ publishings = [FakeBPPH(), FakeBPPH()]
478+ expected_bpph = publishings[1]
479+ expected_bpph.binary_package_name = 'libfoo'
480+ expected_info = (
481+ deb_file_url_for_publication(expected_bpph),
482+ expected_bpph.distro_arch_series.architecture_tag)
483+ self.assertEqual(
484+ [expected_info], publishings_to_package_info(publishings))
485+
486+
487+class TestGetFileContents(TestCase):
488+
489+ def test_exists(self):
490+ expected_content = 'boring content\n'
491+ filename = 'foo.txt'
492+ tree = {filename: {CONTENT: expected_content}}
493+ path = self.useFixture(FileTree(tree)).join(filename)
494+ self.assertEqual(expected_content, get_file_contents(path))
495+
496+ def test_not_exists(self):
497+ tree = {}
498+ path = self.useFixture(FileTree(tree)).join('nothere')
499+ self.assertEqual(None, get_file_contents(path))
500+
501+ def test_directory(self):
502+ tree = {}
503+ path = self.useFixture(FileTree(tree)).path
504+ self.assertRaises(IOError, get_file_contents, path)
505+
506+
507+class TestLibdepMappingFromSymbols(TestCase):
508+
509+ def test_empty(self):
510+ self.assertEqual({}, libdep_mapping_from_symbols(''))
511+
512+ def test_blank_line_ignored(self):
513+ self.assertEqual({}, libdep_mapping_from_symbols('\n'))
514+
515+ def test_alternate_template_ignored(self):
516+ self.assertEqual({}, libdep_mapping_from_symbols('| foo\n'))
517+
518+ def test_meta_information_ignored(self):
519+ self.assertEqual({}, libdep_mapping_from_symbols('* foo\n'))
520+
521+ def test_symbols_ignored(self):
522+ self.assertEqual({}, libdep_mapping_from_symbols(' foo\n'))
523+
524+ def test_comments_ignored(self):
525+ self.assertEqual({}, libdep_mapping_from_symbols('# foo\n'))
526+
527+ def test_include_ignored(self):
528+ self.assertEqual(
529+ {}, libdep_mapping_from_symbols('(arch=!armel)#include foo\n'))
530+
531+ def test_includes_mapping(self):
532+ self.assertEqual(
533+ {'libfoo.so.1': 'libfoo'},
534+ libdep_mapping_from_symbols('libfoo.so.1 libfoo #MINVER#\n'))
535+
536+
537+class TestLibdepServiceClient(TestCase):
538+
539+ TEST_DATA = [('libfoo', {'i386': {'libfoo': 'libfoo-bin'}})]
540+
541+ def test_wraps_libdep_service(self):
542+ double = self.useFixture(LibdepServiceDouble(self.TEST_DATA))
543+ client = Client(double.base_url)
544+ wrapper = LibdepServiceClient(client)
545+ self.assertEqual(
546+ {'libfoo': set(['libfoo-bin'])},
547+ wrapper.get_multiple_dependencies(['libfoo'], 'i386'))
548+ self.assertEqual(
549+ {}, wrapper.get_multiple_dependencies(['libbar'], 'i386'))
550+
551+ def test_has_close_method(self):
552+ client = Client('http://localhost/')
553+ wrapper = LibdepServiceClient(client)
554+ # Check that there is no exception
555+ wrapper.close()

Subscribers

People subscribed via source and target branches