diff options
-rw-r--r-- | tests/test_pkg.py | 99 | ||||
-rwxr-xr-x | whatmaps/command.py | 50 | ||||
-rw-r--r-- | whatmaps/pkg.py | 81 |
3 files changed, 181 insertions, 49 deletions
diff --git a/tests/test_pkg.py b/tests/test_pkg.py new file mode 100644 index 0000000..a8b4182 --- /dev/null +++ b/tests/test_pkg.py @@ -0,0 +1,99 @@ +# vim: set fileencoding=utf-8 : +# (C) 2014 Guido Günther <agx@sigxcpu.org> +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +"""Test L{whatmaps.process} config""" + +import unittest +from mock import patch + +from whatmaps.pkg import Pkg, PkgError + +from . import context + +class TestPkg(unittest.TestCase): + def setUp(self): + self.tmpdir = context.new_tmpdir(__name__) + + def test_abstract(self): + """Check abstract method signatures""" + self.assertIsNone(Pkg.type) + self.assertIsNone(Pkg.services) + + def test_repr(self): + p = Pkg('apckage') + self.assertEqual(str(p), "<None Pkg object name:'apckage'>") + + def test_list_contents(self): + with patch('subprocess.Popen') as mock: + p = Pkg('doesnotmatter') + p._list_contents = '/does/not/matter' + PopenMock = mock.return_value + PopenMock.communicate.return_value = [ + '/package/content', + '/more/package/content', + ] + PopenMock.returncode = 0 + result = p._get_contents() + self.assertIn('/package/content', result) + self.assertNotIn('/more/package/content', result) + + # We want to check that we don't invoke Popen on + # a second call so let it fail + PopenMock.returncode = 1 + + result = p._get_contents() + self.assertIn('/package/content', result) + self.assertNotIn('/more/package/content', result) + + def test_shared_objects(self): + """Test that we properly match shared objects""" + with patch('subprocess.Popen') as mock: + p = Pkg('doesnotmatter') + p._list_contents = '/does/not/matter' + PopenMock = mock.return_value + PopenMock.communicate.return_value = [ + '/lib/foo.so.1', + '/not/a/shared/object', + ] + PopenMock.returncode = 0 + result = p.shared_objects + self.assertIn('/lib/foo.so.1', result) + self.assertNotIn('/not/a/shred/object', result) + + # We want to check that we don't invoke Popen on + # a second call so let it fail. + PopenMock.returncode = 1 + result = p._get_contents() + self.assertIn('/lib/foo.so.1', result) + self.assertNotIn('/not/a/shred/object', result) + + def test_shared_object_error(self): + """Test that we raise PkgError""" + with patch('subprocess.Popen') as mock: + p = Pkg('doesnotmatter') + p._list_contents = '/does/not/matter' + PopenMock = mock.return_value + PopenMock.communicate.return_value = [''] + PopenMock.returncode = 1 + try: + p.shared_objects + self.fail("PkgError exception not raised") + except PkgError: + pass + except Exception as e: + self.fail("Raised '%s is not PkgError" % e) + + def tearDown(self): + context.teardown() diff --git a/whatmaps/command.py b/whatmaps/command.py index 556baf4..461b8e4 100755 --- a/whatmaps/command.py +++ b/whatmaps/command.py @@ -37,55 +37,7 @@ except ImportError: from . process import Process from . distro import Distro - - -class PkgError(Exception): - pass - - -class Pkg(object): - """ - A package in a distribution - @var services: list of services provided by package - @var shared_objects: list of shared objects shipped in this package - @cvar type: package type (e.g. RPM or Debian) - @cvar _so_regex: regex that matches shared objects in the list returned by - _get_contents - @cvar _list_contents: command to list contents of a package, will be passed - to subprocess. "$pkg_name" will be replaced by the package - name. - """ - - type = None - services = None - shared_objects = None - _so_regex = re.compile(r'(?P<so>/.*\.so(\.[^/]*)$)') - _list_contents = None - - def __init__(self, name): - self.name = name - self._services = None - self._shared_objects = None - self._contents = None - - def __repr__(self): - return "<%s Pkg object name:'%s'>" % (self.type, self.name) - - def _get_contents(self): - """List of files in the package""" - if self._contents: - return self._contents - else: - cmd = [ string.Template(arg).substitute(arg, pkg_name=self.name) - for arg in self._list_contents ] - list_contents = subprocess.Popen(cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - output = list_contents.communicate()[0] - if list_contents.returncode: - raise PkgError - self.contents = output.split('\n') - return self.contents +from . pkg import Pkg, PkgError class DebianDistro(Distro): diff --git a/whatmaps/pkg.py b/whatmaps/pkg.py new file mode 100644 index 0000000..b69dd27 --- /dev/null +++ b/whatmaps/pkg.py @@ -0,0 +1,81 @@ +# vim: set fileencoding=utf-8 : +# +# (C) 2010,2014 Guido Günther <agx@sigxcpu.org> +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import re +import string +import subprocess + +class PkgError(Exception): + pass + + +class Pkg(object): + """ + A package in a distribution + @var services: list of services provided by package + @var shared_objects: list of shared objects shipped in this package + @cvar type: package type (e.g. RPM or Debian) + @cvar _so_regex: regex that matches shared objects in the list returned by + _get_contents + @cvar _list_contents: command to list contents of a package, will be passed + to subprocess. "$pkg_name" will be replaced by the package + name. + """ + + type = None + services = None + _so_regex = re.compile(r'(?P<so>/.*\.so(\.[^/]*)$)') + _list_contents = None + + def __init__(self, name): + self.name = name + self._services = None + self._shared_objects = None + self._contents = None + + def __repr__(self): + return "<%s Pkg object name:'%s'>" % (self.type, self.name) + + def _get_contents(self): + """List of files in the package""" + if self._contents: + return self._contents + else: + cmd = [ string.Template(arg).substitute(arg, pkg_name=self.name) + for arg in self._list_contents ] + list_contents = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + output = list_contents.communicate()[0] + if list_contents.returncode: + raise PkgError("Failed to list package contents for '%s'" % self.name) + self._contents = output.split('\n') + return self._contents + + @property + def shared_objects(self): + if self._shared_objects is not None: + return self._shared_objects + + self._shared_objects = [] + contents = self._get_contents() + + for line in contents: + m = self._so_regex.match(line) + if m: + self._shared_objects.append(m.group('so')) + return self._shared_objects |