Example #1
0
    def test_get_system_architecture(self):
        '''get_system_architecture().'''

        arch = impl.get_system_architecture()
        # must be nonempty without line breaks
        self.assertNotEqual(arch, '')
        self.assertTrue('\n' not in arch)
Example #2
0
    def test_get_system_architecture(self):
        '''get_system_architecture().'''

        arch = impl.get_system_architecture()
        # must be nonempty without line breaks
        self.assertNotEqual(arch, '')
        self.assertTrue('\n' not in arch)
Example #3
0
    def test_install_packages_unversioned(self):
        '''install_packages() without versions and no cache'''

        self._setup_foonux_config()
        obsolete = impl.install_packages(self.rootdir, self.configdir,
                                         'Foonux 1.2',
                                         [('coreutils', None),
                                          ('tzdata', None),
                                         ], False, None)

        self.assertEqual(obsolete, '')
        self.assertTrue(os.path.exists(os.path.join(self.rootdir,
                                                    'usr/bin/stat')))
        self.assert_elf_arch(os.path.join(self.rootdir, 'usr/bin/stat'),
                             impl.get_system_architecture())
        self.assertTrue(os.path.exists(os.path.join(self.rootdir,
                                                    'usr/lib/debug/usr/bin/stat')))
        self.assertTrue(os.path.exists(os.path.join(self.rootdir,
                                                    'usr/share/zoneinfo/zone.tab')))

        # does not clobber config dir
        self.assertEqual(os.listdir(self.configdir), ['Foonux 1.2'])
        self.assertEqual(sorted(os.listdir(os.path.join(self.configdir, 'Foonux 1.2'))),
                         ['armhf', 'codename', 'sources.list'])
        self.assertEqual(os.listdir(os.path.join(self.configdir, 'Foonux 1.2', 'armhf')),
                         ['sources.list'])

        # no cache
        self.assertEqual(os.listdir(self.cachedir), [])
    def test_get_file_package_uninstalled(self):
        '''get_file_package() on uninstalled packages.'''

        # determine distro release code name
        lsb_release = subprocess.Popen(['lsb_release', '-sc'],
            stdout=subprocess.PIPE)
        release_name = lsb_release.communicate()[0].decode('UTF-8').strip()
        assert lsb_release.returncode == 0

        # generate a test Contents.gz
        basedir = tempfile.mkdtemp()
        try:
            mapdir = os.path.join(basedir, 'dists', release_name)
            os.makedirs(mapdir)
            with gzip.open(os.path.join(mapdir, 'Contents-%s.gz' %
                impl.get_system_architecture()), 'w') as f:
                f.write(b'''
foo header
FILE                                                    LOCATION
usr/bin/frobnicate                                      foo/frob
usr/bin/frob                                            foo/frob-utils
bo/gu/s                                                 na/mypackage
''')

            self.assertEqual(impl.get_file_package('usr/bin/frob', False, mapdir), None)
            # must not match frob (same file name prefix)
            self.assertEqual(impl.get_file_package('usr/bin/frob', True, mapdir), 'frob-utils')
            self.assertEqual(impl.get_file_package('/usr/bin/frob', True, mapdir), 'frob-utils')

            # invalid mirror
            impl.set_mirror('file:///foo/nonexisting')
            self.assertRaises(IOError, impl.get_file_package, 'usr/bin/frob', True)

            # valid mirror, no cache directory
            impl.set_mirror('file://' + basedir)
            self.assertEqual(impl.get_file_package('usr/bin/frob', True), 'frob-utils')
            self.assertEqual(impl.get_file_package('/usr/bin/frob', True), 'frob-utils')

            # valid mirror, test caching
            cache_dir = os.path.join(basedir, 'cache')
            os.mkdir(cache_dir)
            self.assertEqual(impl.get_file_package('usr/bin/frob', True, cache_dir), 'frob-utils')
            self.assertEqual(len(os.listdir(cache_dir)), 1)
            cache_file = os.listdir(cache_dir)[0]
            self.assertTrue(cache_file.startswith('Contents-'))
            self.assertEqual(impl.get_file_package('/bo/gu/s', True, cache_dir), 'mypackage')

            # valid cache, should not need to access the mirror
            impl.set_mirror('file:///foo/nonexisting')
            self.assertEqual(impl.get_file_package('/bo/gu/s', True, cache_dir), 'mypackage')

            # outdated cache, must refresh the cache and hit the invalid
            # mirror
            now = int(time.time())
            os.utime(os.path.join(cache_dir, cache_file), (now, now-90000))

            self.assertRaises(IOError, impl.get_file_package, '/bo/gu/s', True, cache_dir)
        finally:
            shutil.rmtree(basedir)
    def test_install_packages_unversioned(self):
        '''install_packages() without versions and no cache'''

        self._setup_foonux_config()
        obsolete = impl.install_packages(self.rootdir, self.configdir,
                                         'Foonux 1.2', [
                                             ('coreutils', None),
                                             ('tzdata', None),
                                         ], False, None)

        self.assertEqual(obsolete, '')
        self.assertTrue(
            os.path.exists(os.path.join(self.rootdir, 'usr/bin/stat')))
        self.assert_elf_arch(os.path.join(self.rootdir, 'usr/bin/stat'),
                             impl.get_system_architecture())
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/lib/debug/usr/bin/stat')))
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/share/zoneinfo/zone.tab')))

        # does not clobber config dir
        self.assertEqual(os.listdir(self.configdir), ['Foonux 1.2'])
        self.assertEqual(
            sorted(os.listdir(os.path.join(self.configdir, 'Foonux 1.2'))),
            ['armhf', 'codename', 'sources.list'])
        self.assertEqual(
            os.listdir(os.path.join(self.configdir, 'Foonux 1.2', 'armhf')),
            ['sources.list'])

        # no cache
        self.assertEqual(os.listdir(self.cachedir), [])

        # keeps track of package versions
        with open(os.path.join(self.rootdir, 'packages.txt')) as f:
            pkglist = f.read().splitlines()
        self.assertIn('coreutils 8.13-3ubuntu3', pkglist)
        self.assertIn('coreutils-dbgsym 8.13-3ubuntu3', pkglist)
        self.assertIn('tzdata 2012b-1', pkglist)
        self.assertEqual(len(pkglist), 3, str(pkglist))
    def test_install_packages_versioned(self):
        '''install_packages() with versions and with cache'''

        self._setup_foonux_config(updates=True)
        obsolete = impl.install_packages(
            self.rootdir,
            self.configdir,
            'Foonux 1.2',
            [
                ('coreutils', '8.13-3ubuntu3'),  # should not come from updates
                ('libc6', '2.15-0ubuntu10'),
                ('tzdata', None),  # should come from -updates, > 2012b-1
            ],
            False,
            self.cachedir)

        def sandbox_ver(pkg):
            with gzip.open(
                    os.path.join(self.rootdir, 'usr/share/doc', pkg,
                                 'changelog.Debian.gz')) as f:
                return f.readline().decode().split()[1][1:-1]

        self.assertEqual(obsolete, '')

        # packages get installed
        self.assertTrue(
            os.path.exists(os.path.join(self.rootdir, 'usr/bin/stat')))
        self.assert_elf_arch(os.path.join(self.rootdir, 'usr/bin/stat'),
                             impl.get_system_architecture())
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/lib/debug/usr/bin/stat')))
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/share/zoneinfo/zone.tab')))
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/share/doc/libc6/copyright')))

        # their versions are as expected
        self.assertEqual(sandbox_ver('coreutils'), '8.13-3ubuntu3')
        self.assertEqual(sandbox_ver('libc6'), '2.15-0ubuntu10')
        self.assertEqual(sandbox_ver('libc6-dbg'), '2.15-0ubuntu10')
        self.assertGreater(sandbox_ver('tzdata'), '2012b-1')

        with open(os.path.join(self.rootdir, 'packages.txt')) as f:
            pkglist = f.read().splitlines()
        self.assertIn('coreutils 8.13-3ubuntu3', pkglist)
        self.assertIn('coreutils-dbgsym 8.13-3ubuntu3', pkglist)
        self.assertIn('libc6 2.15-0ubuntu10', pkglist)
        self.assertIn('libc6-dbg 2.15-0ubuntu10', pkglist)
        self.assertIn('tzdata ' + sandbox_ver('tzdata'), pkglist)
        self.assertEqual(len(pkglist), 5, str(pkglist))

        # does not clobber config dir
        self.assertEqual(os.listdir(self.configdir), ['Foonux 1.2'])
        self.assertEqual(
            sorted(os.listdir(os.path.join(self.configdir, 'Foonux 1.2'))),
            ['armhf', 'codename', 'sources.list'])
        self.assertEqual(
            os.listdir(os.path.join(self.configdir, 'Foonux 1.2', 'armhf')),
            ['sources.list'])

        # caches packages, and their versions are as expected
        cache = os.listdir(
            os.path.join(self.cachedir, 'Foonux 1.2', 'apt', 'var', 'cache',
                         'apt', 'archives'))
        cache_versions = {}
        for p in cache:
            try:
                (name, ver) = p.split('_')[:2]
                cache_versions[name] = ver
            except ValueError:
                pass  # not a .deb, ignore
        self.assertEqual(cache_versions['coreutils'], '8.13-3ubuntu3')
        self.assertEqual(cache_versions['coreutils-dbgsym'], '8.13-3ubuntu3')
        self.assertIn('tzdata', cache_versions)
        self.assertEqual(cache_versions['libc6'], '2.15-0ubuntu10')
        self.assertEqual(cache_versions['libc6-dbg'], '2.15-0ubuntu10')

        # installs cached packages
        os.unlink(os.path.join(self.rootdir, 'usr/bin/stat'))
        os.unlink(os.path.join(self.rootdir, 'packages.txt'))
        obsolete = impl.install_packages(self.rootdir, self.configdir,
                                         'Foonux 1.2', [
                                             ('coreutils', '8.13-3ubuntu3'),
                                         ], False, self.cachedir)
        self.assertEqual(obsolete, '')
        self.assertTrue(
            os.path.exists(os.path.join(self.rootdir, 'usr/bin/stat')))

        # complains about obsolete packages
        result = impl.install_packages(self.rootdir, self.configdir,
                                       'Foonux 1.2', [('gnome-common', '1.1')])
        self.assertEqual(len(result.splitlines()), 1)
        self.assertTrue('gnome-common' in result)
        self.assertTrue('1.1' in result)
        # ... but installs the current version anyway
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/bin/gnome-autogen.sh')))
        self.assertGreaterEqual(sandbox_ver('gnome-common'), '3.1.0-0ubuntu1')

        # does not crash on nonexisting packages
        result = impl.install_packages(self.rootdir, self.configdir,
                                       'Foonux 1.2', [('buggerbogger', None)])
        self.assertEqual(len(result.splitlines()), 1)
        self.assertTrue('buggerbogger' in result)
        self.assertTrue('not exist' in result)

        # can interleave with other operations
        dpkg = subprocess.Popen(['dpkg-query', '-Wf${Version}', 'dash'],
                                stdout=subprocess.PIPE)
        dash_version = dpkg.communicate()[0].decode()
        self.assertEqual(dpkg.returncode, 0)

        self.assertEqual(impl.get_version('dash'), dash_version)
        self.assertRaises(ValueError, impl.get_available_version,
                          'buggerbogger')

        # still installs packages after above operations
        os.unlink(os.path.join(self.rootdir, 'usr/bin/stat'))
        os.unlink(os.path.join(self.rootdir, 'packages.txt'))
        impl.install_packages(self.rootdir, self.configdir, 'Foonux 1.2', [
            ('coreutils', '8.13-3ubuntu3'),
            ('dpkg', None),
        ], False, self.cachedir)
        self.assertTrue(
            os.path.exists(os.path.join(self.rootdir, 'usr/bin/stat')))
        self.assertTrue(
            os.path.exists(os.path.join(self.rootdir, 'usr/bin/dpkg')))
class T(unittest.TestCase):
    def setUp(self):
        # save and restore configuration file
        self.orig_conf = impl.configuration
        self.workdir = tempfile.mkdtemp()

        try:
            impl.get_available_version('coreutils-dbgsym')
            self.has_dbgsym = True
        except ValueError:
            self.has_dbgsym = False

    def tearDown(self):
        impl.configuration = self.orig_conf
        shutil.rmtree(self.workdir)

    def test_check_files_md5(self):
        '''_check_files_md5().'''

        td = tempfile.mkdtemp()
        try:
            f1 = os.path.join(td, 'test 1.txt')
            f2 = os.path.join(td, 'test:2.txt')
            sumfile = os.path.join(td, 'sums.txt')
            with open(f1, 'w') as fd:
                fd.write('Some stuff')
            with open(f2, 'w') as fd:
                fd.write('More stuff')
            # use one relative and one absolute path in checksums file
            with open(sumfile, 'wb') as fd:
                fd.write(b'2e41290da2fa3f68bd3313174467e3b5  ' +
                         f1[1:].encode() + b'\n')
                fd.write(b'f6423dfbc4faf022e58b4d3f5ff71a70  ' + f2.encode() +
                         b'\n')
                fd.write(b'deadbeef000001111110000011110000  /bin/\xc3\xa4')
            self.assertEqual(impl._check_files_md5(sumfile), [],
                             'correct md5sums')

            with open(f1, 'w') as fd:
                fd.write('Some stuff!')
            self.assertEqual(impl._check_files_md5(sumfile), [f1[1:]],
                             'file 1 wrong')
            with open(f2, 'w') as fd:
                fd.write('More stuff!')
            self.assertEqual(impl._check_files_md5(sumfile), [f1[1:], f2],
                             'files 1 and 2 wrong')
            with open(f1, 'w') as fd:
                fd.write('Some stuff')
            self.assertEqual(impl._check_files_md5(sumfile), [f2],
                             'file 2 wrong')

            # check using a direct md5 list as argument
            with open(sumfile, 'rb') as fd:
                self.assertEqual(impl._check_files_md5(fd.read()), [f2],
                                 'file 2 wrong')

        finally:
            shutil.rmtree(td)

    def test_get_version(self):
        '''get_version().'''

        self.assertTrue(impl.get_version('libc6').startswith('2'))
        self.assertRaises(ValueError, impl.get_version, 'nonexisting')
        self.assertRaises(ValueError, impl.get_version, 'wukrainian')

    def test_get_available_version(self):
        '''get_available_version().'''

        self.assertTrue(impl.get_available_version('libc6').startswith('2'))
        self.assertRaises(ValueError, impl.get_available_version,
                          'nonexisting')

    def test_get_dependencies(self):
        '''get_dependencies().'''

        # package with both Depends: and Pre-Depends:
        d = impl.get_dependencies('bash')
        self.assertTrue(len(d) > 2)
        self.assertTrue('libc6' in d)
        for dep in d:
            self.assertTrue(impl.get_version(dep))

        # Pre-Depends: only
        d = impl.get_dependencies('coreutils')
        self.assertTrue(len(d) >= 1)
        self.assertTrue('libc6' in d)
        for dep in d:
            self.assertTrue(impl.get_version(dep))

        # Depends: only
        d = impl.get_dependencies('libc6')
        self.assertTrue(len(d) >= 1)
        for dep in d:
            self.assertTrue(impl.get_version(dep))

    def test_get_source(self):
        '''get_source().'''

        self.assertRaises(ValueError, impl.get_source, 'nonexisting')
        self.assertEqual(impl.get_source('bash'), 'bash')
        self.assertTrue('glibc' in impl.get_source('libc6'))

    def test_get_package_origin(self):
        '''get_package_origin().'''

        # determine distro name
        distro = impl.get_os_version()[0]

        self.assertRaises(ValueError, impl.get_package_origin, 'nonexisting')
        # this assumes that this package is not installed
        self.assertRaises(ValueError, impl.get_package_origin, 'robocode-doc')
        # this assumes that bash is native
        self.assertEqual(impl.get_package_origin('bash'), distro)
        # no non-native test here, hard to come up with a generic one

    def test_is_distro_package(self):
        '''is_distro_package().'''

        self.assertRaises(ValueError, impl.is_distro_package, 'nonexisting')
        self.assertTrue(impl.is_distro_package('bash'))
        # no False test here, hard to come up with a generic one

    def test_get_architecture(self):
        '''get_architecture().'''

        self.assertRaises(ValueError, impl.get_architecture, 'nonexisting')
        # just assume that bash uses the native architecture
        d = subprocess.Popen(['dpkg', '--print-architecture'],
                             stdout=subprocess.PIPE)
        system_arch = d.communicate()[0].decode().strip()
        assert d.returncode == 0
        self.assertEqual(impl.get_architecture('bash'), system_arch)

    def test_get_files(self):
        '''get_files().'''

        self.assertRaises(ValueError, impl.get_files, 'nonexisting')
        self.assertTrue('/bin/bash' in impl.get_files('bash'))

    def test_get_file_package(self):
        '''get_file_package() on installed files.'''

        self.assertEqual(impl.get_file_package('/bin/bash'), 'bash')
        self.assertEqual(impl.get_file_package('/bin/cat'), 'coreutils')
        self.assertEqual(impl.get_file_package('/etc/pam.conf'),
                         'libpam-runtime')
        self.assertEqual(impl.get_file_package('/nonexisting'), None)

    def test_get_file_package_uninstalled(self):
        '''get_file_package() on uninstalled packages.'''

        # generate a test Contents.gz
        basedir = tempfile.mkdtemp()
        try:
            # test Contents.gz for release pocket
            mapdir = os.path.join(basedir, 'dists', impl.get_distro_codename())
            os.makedirs(mapdir)
            with gzip.open(
                    os.path.join(
                        mapdir,
                        'Contents-%s.gz' % impl.get_system_architecture()),
                    'w') as f:
                f.write(b'''
foo header
FILE                                                    LOCATION
usr/bin/frobnicate                                      foo/frob
usr/bin/frob                                            foo/frob-utils
bo/gu/s                                                 na/mypackage
bin/true                                                admin/superutils
''')

            # test Contents.gz for -updates pocket
            mapdir = os.path.join(basedir, 'dists',
                                  impl.get_distro_codename() + '-updates')
            os.makedirs(mapdir)
            with gzip.open(
                    os.path.join(
                        mapdir,
                        'Contents-%s.gz' % impl.get_system_architecture()),
                    'w') as f:
                f.write(b'''
foo header
FILE                                                    LOCATION
lib/libnew.so.5                                         universe/libs/libnew5
''')

            # use this as a mirror
            impl.set_mirror('file://' + basedir)

            self.assertEqual(impl.get_file_package('usr/bin/frob', False),
                             None)
            # must not match frob (same file name prefix)
            self.assertEqual(impl.get_file_package('usr/bin/frob', True),
                             'frob-utils')
            self.assertEqual(impl.get_file_package('/usr/bin/frob', True),
                             'frob-utils')
            # find files from -updates pocket
            self.assertEqual(impl.get_file_package('/lib/libnew.so.5', False),
                             None)
            self.assertEqual(impl.get_file_package('/lib/libnew.so.5', True),
                             'libnew5')

            # invalid mirror
            impl.set_mirror('file:///foo/nonexisting')
            self.assertRaises(IOError, impl.get_file_package, 'usr/bin/frob',
                              True)

            # valid mirror, test cache directory
            impl.set_mirror('file://' + basedir)
            cache_dir = os.path.join(basedir, 'cache')
            os.mkdir(cache_dir)
            self.assertEqual(
                impl.get_file_package('usr/bin/frob', True, cache_dir),
                'frob-utils')
            cache_dir_files = os.listdir(cache_dir)
            self.assertEqual(len(cache_dir_files), 2)
            self.assertEqual(
                impl.get_file_package('/bo/gu/s', True, cache_dir),
                'mypackage')

            # valid cache, should not need to access the mirror
            impl.set_mirror('file:///foo/nonexisting')
            self.assertEqual(
                impl.get_file_package('/bin/true', True, cache_dir),
                'superutils')
            self.assertEqual(
                impl.get_file_package('/bo/gu/s', True, cache_dir),
                'mypackage')
            self.assertEqual(
                impl.get_file_package('/lib/libnew.so.5', True, cache_dir),
                'libnew5')

            # outdated cache, must refresh the cache and hit the invalid
            # mirror
            if 'updates' in cache_dir_files[0]:
                cache_file = cache_dir_files[1]
            else:
                cache_file = cache_dir_files[0]
            now = int(time.time())
            os.utime(os.path.join(cache_dir, cache_file), (now, now - 90000))

            self.assertRaises(IOError, impl.get_file_package, '/bo/gu/s', True,
                              cache_dir)
        finally:
            shutil.rmtree(basedir)

    def test_get_file_package_uninstalled_multiarch(self):
        '''get_file_package() on foreign arches and releases'''

        # map "Foonux 3.14" to "mocky"
        orig_distro_release_to_codename = impl._distro_release_to_codename
        impl._distro_release_to_codename = lambda r: (r == 'Foonux 3.14'
                                                      ) and 'mocky' or None

        # generate test Contents.gz for two fantasy architectures
        basedir = tempfile.mkdtemp()
        try:
            mapdir = os.path.join(basedir, 'dists', impl.get_distro_codename())
            os.makedirs(mapdir)
            with gzip.open(os.path.join(mapdir, 'Contents-even.gz'), 'w') as f:
                f.write(b'''
foo header
FILE                                                    LOCATION
usr/lib/even/libfrob.so.1                               foo/libfrob1
usr/bin/frob                                            foo/frob-utils
''')
            with gzip.open(os.path.join(mapdir, 'Contents-odd.gz'), 'w') as f:
                f.write(b'''
foo header
FILE                                                    LOCATION
usr/lib/odd/libfrob.so.1                                foo/libfrob1
usr/bin/frob                                            foo/frob-utils
''')

            # and another one for fantasy release
            os.mkdir(os.path.join(basedir, 'dists', 'mocky'))
            with gzip.open(
                    os.path.join(basedir, 'dists', 'mocky',
                                 'Contents-even.gz'), 'w') as f:
                f.write(b'''
foo header
FILE                                                    LOCATION
usr/lib/even/libfrob.so.0                               foo/libfrob0
usr/bin/frob                                            foo/frob
''')

            # use this as a mirror
            impl.set_mirror('file://' + basedir)

            # must not match system architecture
            self.assertEqual(impl.get_file_package('usr/bin/frob', False),
                             None)
            # must match correct architecture
            self.assertEqual(
                impl.get_file_package('usr/bin/frob', True, arch='even'),
                'frob-utils')
            self.assertEqual(
                impl.get_file_package('usr/bin/frob', True, arch='odd'),
                'frob-utils')
            self.assertEqual(
                impl.get_file_package('/usr/lib/even/libfrob.so.1',
                                      True,
                                      arch='even'), 'libfrob1')
            self.assertEqual(
                impl.get_file_package('/usr/lib/even/libfrob.so.1',
                                      True,
                                      arch='odd'), None)
            self.assertEqual(
                impl.get_file_package('/usr/lib/odd/libfrob.so.1',
                                      True,
                                      arch='odd'), 'libfrob1')

            # for mocky release ("Foonux 3.14")
            self.assertEqual(
                impl.get_file_package('/usr/lib/even/libfrob.so.1',
                                      True,
                                      release='Foonux 3.14',
                                      arch='even'), None)
            self.assertEqual(
                impl.get_file_package('/usr/lib/even/libfrob.so.0',
                                      True,
                                      release='Foonux 3.14',
                                      arch='even'), 'libfrob0')
            self.assertEqual(
                impl.get_file_package('/usr/bin/frob',
                                      True,
                                      release='Foonux 3.14',
                                      arch='even'), 'frob')

            # invalid mirror
            impl.set_mirror('file:///foo/nonexisting')
            self.assertRaises(IOError,
                              impl.get_file_package,
                              '/usr/lib/even/libfrob.so.1',
                              True,
                              arch='even')
            self.assertRaises(IOError,
                              impl.get_file_package,
                              '/usr/lib/even/libfrob.so.0',
                              True,
                              release='Foonux 3.14',
                              arch='even')

            # valid mirror, test caching
            impl.set_mirror('file://' + basedir)
            cache_dir = os.path.join(basedir, 'cache')
            os.mkdir(cache_dir)
            self.assertEqual(
                impl.get_file_package('/usr/lib/even/libfrob.so.1',
                                      True,
                                      cache_dir,
                                      arch='even'), 'libfrob1')
            self.assertEqual(len(os.listdir(cache_dir)), 1)
            cache_file = os.listdir(cache_dir)[0]

            self.assertEqual(
                impl.get_file_package('/usr/lib/even/libfrob.so.0',
                                      True,
                                      cache_dir,
                                      release='Foonux 3.14',
                                      arch='even'), 'libfrob0')
            self.assertEqual(len(os.listdir(cache_dir)), 2)

            # valid cache, should not need to access the mirror
            impl.set_mirror('file:///foo/nonexisting')
            self.assertEqual(
                impl.get_file_package('usr/bin/frob',
                                      True,
                                      cache_dir,
                                      arch='even'), 'frob-utils')
            self.assertEqual(
                impl.get_file_package('usr/bin/frob',
                                      True,
                                      cache_dir,
                                      release='Foonux 3.14',
                                      arch='even'), 'frob')

            # but no cached file for the other arch
            self.assertRaises(IOError,
                              impl.get_file_package,
                              'usr/bin/frob',
                              True,
                              cache_dir,
                              arch='odd')

            # outdated cache, must refresh the cache and hit the invalid
            # mirror
            now = int(time.time())
            os.utime(os.path.join(cache_dir, cache_file), (now, now - 90000))

            self.assertRaises(IOError,
                              impl.get_file_package,
                              'usr/bin/frob',
                              True,
                              cache_dir,
                              arch='even')
        finally:
            shutil.rmtree(basedir)
            impl._distro_release_to_codename = orig_distro_release_to_codename

    def test_get_file_package_diversion(self):
        '''get_file_package() for a diverted file.'''

        # pick first diversion we have
        p = subprocess.Popen('LC_ALL=C dpkg-divert --list | head -n 1',
                             shell=True,
                             stdout=subprocess.PIPE)
        out = p.communicate()[0].decode('UTF-8')
        assert p.returncode == 0
        assert out
        fields = out.split()
        file = fields[2]
        pkg = fields[-1]

        self.assertEqual(impl.get_file_package(file), pkg)

    def test_mirror_from_apt_sources(self):
        s = os.path.join(self.workdir, 'sources.list')

        # valid file, should grab the first mirror
        with open(s, 'w') as f:
            f.write('''# some comment
deb-src http://source.mirror/foo tuxy main
deb http://binary.mirror/tuxy tuxy main
deb http://secondary.mirror tuxy extra
''')
            f.flush()
            self.assertEqual(impl._get_primary_mirror_from_apt_sources(s),
                             'http://binary.mirror/tuxy')

        # valid file with options
        with open(s, 'w') as f:
            f.write('''# some comment
deb-src http://source.mirror/foo tuxy main
deb [arch=flowerpc,leghf] http://binary.mirror/tuxy tuxy main
deb http://secondary.mirror tuxy extra
''')
            f.flush()
            self.assertEqual(impl._get_primary_mirror_from_apt_sources(s),
                             'http://binary.mirror/tuxy')

        # empty file
        with open(s, 'w') as f:
            f.flush()
        self.assertRaises(SystemError,
                          impl._get_primary_mirror_from_apt_sources, s)

    def test_get_modified_conffiles(self):
        '''get_modified_conffiles()'''

        # very shallow
        self.assertEqual(type(impl.get_modified_conffiles('bash')), type({}))
        self.assertEqual(type(impl.get_modified_conffiles('apport')), type({}))
        self.assertEqual(type(impl.get_modified_conffiles('nonexisting')),
                         type({}))

    def test_get_system_architecture(self):
        '''get_system_architecture().'''

        arch = impl.get_system_architecture()
        # must be nonempty without line breaks
        self.assertNotEqual(arch, '')
        self.assertTrue('\n' not in arch)

    def test_get_library_paths(self):
        '''get_library_paths().'''

        paths = impl.get_library_paths()
        # must be nonempty without line breaks
        self.assertNotEqual(paths, '')
        self.assertTrue(':' in paths)
        self.assertTrue('/lib' in paths)
        self.assertTrue('\n' not in paths)

    def test_compare_versions(self):
        '''compare_versions.'''

        self.assertEqual(impl.compare_versions('1', '2'), -1)
        self.assertEqual(impl.compare_versions('1.0-1ubuntu1', '1.0-1ubuntu2'),
                         -1)
        self.assertEqual(impl.compare_versions('1.0-1ubuntu1', '1.0-1ubuntu1'),
                         0)
        self.assertEqual(impl.compare_versions('1.0-1ubuntu2', '1.0-1ubuntu1'),
                         1)
        self.assertEqual(impl.compare_versions('1:1.0-1', '2007-2'), 1)
        self.assertEqual(impl.compare_versions('1:1.0-1~1', '1:1.0-1'), -1)

    def test_enabled(self):
        '''enabled.'''

        impl.configuration = '/nonexisting'
        self.assertEqual(impl.enabled(), True)

        f = tempfile.NamedTemporaryFile()
        impl.configuration = f.name
        f.write('# configuration file\nenabled = 1'.encode())
        f.flush()
        self.assertEqual(impl.enabled(), True)
        f.close()

        f = tempfile.NamedTemporaryFile()
        impl.configuration = f.name
        f.write('# configuration file\n  enabled =0  '.encode())
        f.flush()
        self.assertEqual(impl.enabled(), False)
        f.close()

        f = tempfile.NamedTemporaryFile()
        impl.configuration = f.name
        f.write('# configuration file\nnothing here'.encode())
        f.flush()
        self.assertEqual(impl.enabled(), True)
        f.close()

    def test_get_kernel_package(self):
        '''get_kernel_package().'''

        self.assertTrue('linux' in impl.get_kernel_package())

    def test_package_name_glob(self):
        '''package_name_glob().'''

        self.assertTrue(len(impl.package_name_glob('a*')) > 5)
        self.assertTrue('bash' in impl.package_name_glob('ba*h'))
        self.assertEqual(impl.package_name_glob('bash'), ['bash'])
        self.assertEqual(impl.package_name_glob('xzywef*'), [])

    @unittest.skipUnless(_has_internet(), 'online test')
    def test_install_packages_versioned(self):
        '''install_packages() with versions and with cache'''

        self._setup_foonux_config(updates=True)
        obsolete = impl.install_packages(
            self.rootdir,
            self.configdir,
            'Foonux 1.2',
            [
                ('coreutils', '8.13-3ubuntu3'),  # should not come from updates
                ('libc6', '2.15-0ubuntu10'),
                ('tzdata', None),  # should come from -updates, > 2012b-1
            ],
            False,
            self.cachedir)

        def sandbox_ver(pkg):
            with gzip.open(
                    os.path.join(self.rootdir, 'usr/share/doc', pkg,
                                 'changelog.Debian.gz')) as f:
                return f.readline().decode().split()[1][1:-1]

        self.assertEqual(obsolete, '')

        # packages get installed
        self.assertTrue(
            os.path.exists(os.path.join(self.rootdir, 'usr/bin/stat')))
        self.assert_elf_arch(os.path.join(self.rootdir, 'usr/bin/stat'),
                             impl.get_system_architecture())
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/lib/debug/usr/bin/stat')))
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/share/zoneinfo/zone.tab')))
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/share/doc/libc6/copyright')))

        # their versions are as expected
        self.assertEqual(sandbox_ver('coreutils'), '8.13-3ubuntu3')
        self.assertEqual(sandbox_ver('libc6'), '2.15-0ubuntu10')
        self.assertEqual(sandbox_ver('libc6-dbg'), '2.15-0ubuntu10')
        self.assertGreater(sandbox_ver('tzdata'), '2012b-1')

        with open(os.path.join(self.rootdir, 'packages.txt')) as f:
            pkglist = f.read().splitlines()
        self.assertIn('coreutils 8.13-3ubuntu3', pkglist)
        self.assertIn('coreutils-dbgsym 8.13-3ubuntu3', pkglist)
        self.assertIn('libc6 2.15-0ubuntu10', pkglist)
        self.assertIn('libc6-dbg 2.15-0ubuntu10', pkglist)
        self.assertIn('tzdata ' + sandbox_ver('tzdata'), pkglist)
        self.assertEqual(len(pkglist), 5, str(pkglist))

        # does not clobber config dir
        self.assertEqual(os.listdir(self.configdir), ['Foonux 1.2'])
        self.assertEqual(
            sorted(os.listdir(os.path.join(self.configdir, 'Foonux 1.2'))),
            ['armhf', 'codename', 'sources.list'])
        self.assertEqual(
            os.listdir(os.path.join(self.configdir, 'Foonux 1.2', 'armhf')),
            ['sources.list'])

        # caches packages, and their versions are as expected
        cache = os.listdir(
            os.path.join(self.cachedir, 'Foonux 1.2', 'apt', 'var', 'cache',
                         'apt', 'archives'))
        cache_versions = {}
        for p in cache:
            try:
                (name, ver) = p.split('_')[:2]
                cache_versions[name] = ver
            except ValueError:
                pass  # not a .deb, ignore
        self.assertEqual(cache_versions['coreutils'], '8.13-3ubuntu3')
        self.assertEqual(cache_versions['coreutils-dbgsym'], '8.13-3ubuntu3')
        self.assertIn('tzdata', cache_versions)
        self.assertEqual(cache_versions['libc6'], '2.15-0ubuntu10')
        self.assertEqual(cache_versions['libc6-dbg'], '2.15-0ubuntu10')

        # installs cached packages
        os.unlink(os.path.join(self.rootdir, 'usr/bin/stat'))
        os.unlink(os.path.join(self.rootdir, 'packages.txt'))
        obsolete = impl.install_packages(self.rootdir, self.configdir,
                                         'Foonux 1.2', [
                                             ('coreutils', '8.13-3ubuntu3'),
                                         ], False, self.cachedir)
        self.assertEqual(obsolete, '')
        self.assertTrue(
            os.path.exists(os.path.join(self.rootdir, 'usr/bin/stat')))

        # complains about obsolete packages
        result = impl.install_packages(self.rootdir, self.configdir,
                                       'Foonux 1.2', [('gnome-common', '1.1')])
        self.assertEqual(len(result.splitlines()), 1)
        self.assertTrue('gnome-common' in result)
        self.assertTrue('1.1' in result)
        # ... but installs the current version anyway
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/bin/gnome-autogen.sh')))
        self.assertGreaterEqual(sandbox_ver('gnome-common'), '3.1.0-0ubuntu1')

        # does not crash on nonexisting packages
        result = impl.install_packages(self.rootdir, self.configdir,
                                       'Foonux 1.2', [('buggerbogger', None)])
        self.assertEqual(len(result.splitlines()), 1)
        self.assertTrue('buggerbogger' in result)
        self.assertTrue('not exist' in result)

        # can interleave with other operations
        dpkg = subprocess.Popen(['dpkg-query', '-Wf${Version}', 'dash'],
                                stdout=subprocess.PIPE)
        dash_version = dpkg.communicate()[0].decode()
        self.assertEqual(dpkg.returncode, 0)

        self.assertEqual(impl.get_version('dash'), dash_version)
        self.assertRaises(ValueError, impl.get_available_version,
                          'buggerbogger')

        # still installs packages after above operations
        os.unlink(os.path.join(self.rootdir, 'usr/bin/stat'))
        os.unlink(os.path.join(self.rootdir, 'packages.txt'))
        impl.install_packages(self.rootdir, self.configdir, 'Foonux 1.2', [
            ('coreutils', '8.13-3ubuntu3'),
            ('dpkg', None),
        ], False, self.cachedir)
        self.assertTrue(
            os.path.exists(os.path.join(self.rootdir, 'usr/bin/stat')))
        self.assertTrue(
            os.path.exists(os.path.join(self.rootdir, 'usr/bin/dpkg')))

    @unittest.skipUnless(_has_internet(), 'online test')
    def test_install_packages_unversioned(self):
        '''install_packages() without versions and no cache'''

        self._setup_foonux_config()
        obsolete = impl.install_packages(self.rootdir, self.configdir,
                                         'Foonux 1.2', [
                                             ('coreutils', None),
                                             ('tzdata', None),
                                         ], False, None)

        self.assertEqual(obsolete, '')
        self.assertTrue(
            os.path.exists(os.path.join(self.rootdir, 'usr/bin/stat')))
        self.assert_elf_arch(os.path.join(self.rootdir, 'usr/bin/stat'),
                             impl.get_system_architecture())
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/lib/debug/usr/bin/stat')))
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/share/zoneinfo/zone.tab')))

        # does not clobber config dir
        self.assertEqual(os.listdir(self.configdir), ['Foonux 1.2'])
        self.assertEqual(
            sorted(os.listdir(os.path.join(self.configdir, 'Foonux 1.2'))),
            ['armhf', 'codename', 'sources.list'])
        self.assertEqual(
            os.listdir(os.path.join(self.configdir, 'Foonux 1.2', 'armhf')),
            ['sources.list'])

        # no cache
        self.assertEqual(os.listdir(self.cachedir), [])

        # keeps track of package versions
        with open(os.path.join(self.rootdir, 'packages.txt')) as f:
            pkglist = f.read().splitlines()
        self.assertIn('coreutils 8.13-3ubuntu3', pkglist)
        self.assertIn('coreutils-dbgsym 8.13-3ubuntu3', pkglist)
        self.assertIn('tzdata 2012b-1', pkglist)
        self.assertEqual(len(pkglist), 3, str(pkglist))

    @unittest.skipUnless(_has_internet(), 'online test')
    def test_install_packages_system(self):
        '''install_packages() with system configuration'''

        # trigger an unrelated package query here to get the cache set up,
        # reproducing an install failure when the internal caches are not
        # reset properly
        impl.get_version('dash')

        self._setup_foonux_config()
        result = impl.install_packages(self.rootdir, None, None, [
            ('coreutils', impl.get_version('coreutils')),
            ('tzdata', '1.1'),
        ], False, self.cachedir)

        self.assertTrue(
            os.path.exists(os.path.join(self.rootdir, 'usr/bin/stat')))
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/share/zoneinfo/zone.tab')))

        # complains about obsolete packages
        self.assertGreaterEqual(len(result.splitlines()), 1)
        self.assertTrue('tzdata' in result)
        self.assertTrue('1.1' in result)

        # caches packages
        cache = os.listdir(
            os.path.join(self.cachedir, 'system', 'apt', 'var', 'cache', 'apt',
                         'archives'))
        cache_names = [p.split('_')[0] for p in cache]
        self.assertTrue('coreutils' in cache_names)
        self.assertEqual('coreutils-dbgsym' in cache_names, self.has_dbgsym)
        self.assertTrue('tzdata' in cache_names)

        # works with relative paths and existing cache
        os.unlink(os.path.join(self.rootdir, 'usr/bin/stat'))
        os.unlink(os.path.join(self.rootdir, 'packages.txt'))
        orig_cwd = os.getcwd()
        try:
            os.chdir(self.workdir)
            impl.install_packages('root', None, None, [('coreutils', None)],
                                  False, 'cache')
        finally:
            os.chdir(orig_cwd)
            self.assertTrue(
                os.path.exists(os.path.join(self.rootdir, 'usr/bin/stat')))

    @unittest.skipUnless(_has_internet(), 'online test')
    def test_install_packages_error(self):
        '''install_packages() with errors'''

        # sources.list with invalid format
        self._setup_foonux_config()
        with open(os.path.join(self.configdir, 'Foonux 1.2', 'sources.list'),
                  'w') as f:
            f.write('bogus format')

        try:
            impl.install_packages(self.rootdir, self.configdir, 'Foonux 1.2',
                                  [('tzdata', None)], False, self.cachedir)
            self.fail(
                'install_packages() unexpectedly succeeded with broken sources.list'
            )
        except SystemError as e:
            self.assertTrue('bogus' in str(e))
            self.assertFalse('Exception' in str(e))

        # sources.list with wrong server
        with open(os.path.join(self.configdir, 'Foonux 1.2', 'sources.list'),
                  'w') as f:
            f.write(
                'deb http://archive.ubuntu.com/nosuchdistro/ precise main\n')

        try:
            impl.install_packages(self.rootdir, self.configdir, 'Foonux 1.2',
                                  [('tzdata', None)], False, self.cachedir)
            self.fail(
                'install_packages() unexpectedly succeeded with broken server URL'
            )
        except SystemError as e:
            self.assertTrue('nosuchdistro' in str(e), str(e))
            self.assertTrue('index files failed to download' in str(e))

    @unittest.skipUnless(_has_internet(), 'online test')
    def test_install_packages_permanent_sandbox(self):
        '''install_packages() with a permanent sandbox'''

        self._setup_foonux_config()
        zonetab = os.path.join(self.rootdir, 'usr/share/zoneinfo/zone.tab')

        impl.install_packages(self.rootdir,
                              self.configdir,
                              'Foonux 1.2', [('tzdata', None)],
                              False,
                              self.cachedir,
                              permanent_rootdir=True)

        # This will now be using a Cache with our rootdir.
        archives = apt_pkg.config.find_dir('Dir::Cache::archives')
        tzdata = glob.glob(os.path.join(archives, 'tzdata*.deb'))
        if not tzdata:
            self.fail('tzdata was not downloaded')
        tzdata_written = os.path.getctime(tzdata[0])
        zonetab_written = os.path.getctime(zonetab)

        impl.install_packages(self.rootdir,
                              self.configdir,
                              'Foonux 1.2', [('coreutils', None),
                                             ('tzdata', None)],
                              False,
                              self.cachedir,
                              permanent_rootdir=True)

        if not glob.glob(os.path.join(archives, 'coreutils*.deb')):
            self.fail('coreutils was not downloaded.')
            self.assertEqual(os.path.getctime(tzdata[0]), tzdata_written,
                             'tzdata downloaded twice.')
            self.assertEqual(zonetab_written, os.path.getctime(zonetab),
                             'zonetab written twice.')
            self.assertTrue(
                os.path.exists(os.path.join(self.rootdir, 'usr/bin/stat')))

        # Prevent packages from downloading.
        apt_pkg.config.set('Acquire::http::Proxy', 'http://nonexistent')
        self.assertRaises(SystemExit,
                          impl.install_packages,
                          self.rootdir,
                          self.configdir,
                          'Foonux 1.2', [('libc6', None)],
                          False,
                          self.cachedir,
                          permanent_rootdir=True)
        # These packages exist, so attempting to install them should not fail.
        impl.install_packages(self.rootdir,
                              self.configdir,
                              'Foonux 1.2', [('coreutils', None),
                                             ('tzdata', None)],
                              False,
                              self.cachedir,
                              permanent_rootdir=True)
        # even without cached debs, trying to install the same versions should
        # be a no-op and succeed
        for f in glob.glob(
                '%s/Foonux 1.2/apt/var/cache/apt/archives/coreutils*' %
                self.cachedir):
            os.unlink(f)
        impl.install_packages(self.rootdir,
                              self.configdir,
                              'Foonux 1.2', [('coreutils', None)],
                              False,
                              self.cachedir,
                              permanent_rootdir=True)

        # trying to install another package should fail, though
        self.assertRaises(SystemExit,
                          impl.install_packages,
                          self.rootdir,
                          self.configdir,
                          'Foonux 1.2', [('aspell-doc', None)],
                          False,
                          self.cachedir,
                          permanent_rootdir=True)

        apt_pkg.config.set('Acquire::http::Proxy', '')

    @unittest.skipUnless(_has_internet(), 'online test')
    def test_install_packages_permanent_sandbox_repack(self):
        self._setup_foonux_config()
        apache_bin_path = os.path.join(self.rootdir, 'usr/sbin/apache2')
        impl.install_packages(self.rootdir,
                              self.configdir,
                              'Foonux 1.2', [('apache2-mpm-worker', None)],
                              False,
                              self.cachedir,
                              permanent_rootdir=True)
        self.assertTrue(
            os.readlink(apache_bin_path).endswith('mpm-worker/apache2'))
        impl.install_packages(self.rootdir,
                              self.configdir,
                              'Foonux 1.2', [('apache2-mpm-event', None)],
                              False,
                              self.cachedir,
                              permanent_rootdir=True)
        self.assertTrue(
            os.readlink(apache_bin_path).endswith('mpm-event/apache2'),
            'should have installed mpm-event, but have mpm-worker.')
        impl.install_packages(self.rootdir,
                              self.configdir,
                              'Foonux 1.2', [('apache2-mpm-worker', None)],
                              False,
                              self.cachedir,
                              permanent_rootdir=True)
        self.assertTrue(
            os.readlink(apache_bin_path).endswith('mpm-worker/apache2'),
            'should have installed mpm-worker, but have mpm-event.')

    @unittest.skipUnless(_has_internet(), 'online test')
    @unittest.skipIf(impl.get_system_architecture() == 'armhf',
                     'native armhf architecture')
    def test_install_packages_armhf(self):
        '''install_packages() for foreign architecture armhf'''

        self._setup_foonux_config()
        obsolete = impl.install_packages(self.rootdir,
                                         self.configdir,
                                         'Foonux 1.2', [
                                             ('coreutils', '8.13-3ubuntu3'),
                                             ('libc6', '2.15-0ubuntu9'),
                                         ],
                                         False,
                                         self.cachedir,
                                         architecture='armhf')

        self.assertEqual(
            obsolete,
            'libc6 version 2.15-0ubuntu9 required, but 2.15-0ubuntu10 is available\n'
        )

        self.assertTrue(
            os.path.exists(os.path.join(self.rootdir, 'usr/bin/stat')))
        self.assert_elf_arch(os.path.join(self.rootdir, 'usr/bin/stat'),
                             'armhf')
        self.assertTrue(
            os.path.exists(
                os.path.join(self.rootdir, 'usr/share/doc/libc6/copyright')))

        # caches packages
        cache = os.listdir(
            os.path.join(self.cachedir, 'Foonux 1.2', 'apt', 'var', 'cache',
                         'apt', 'archives'))
        self.assertTrue('coreutils_8.13-3ubuntu3_armhf.deb' in cache, cache)
        self.assertTrue('libc6_2.15-0ubuntu10_armhf.deb' in cache, cache)

    @unittest.skipUnless(_has_internet(), 'online test')
    def test_get_source_tree_sandbox(self):
        self._setup_foonux_config()
        out_dir = os.path.join(self.workdir, 'out')
        os.mkdir(out_dir)
        impl._build_apt_sandbox(
            self.rootdir,
            os.path.join(self.configdir, 'Foonux 1.2', 'sources.list'))
        res = impl.get_source_tree('base-files',
                                   out_dir,
                                   sandbox=self.rootdir,
                                   apt_update=True)
        self.assertTrue(os.path.isdir(os.path.join(res, 'debian')))
        # this needs to be updated when the release in _setup_foonux_config
        # changes
        self.assertTrue(res.endswith('/base-files-6.5ubuntu6'),
                        'unexpected version: ' + res.split('/')[-1])

    def _setup_foonux_config(self, updates=False):
        '''Set up directories and configuration for install_packages()'''

        self.cachedir = os.path.join(self.workdir, 'cache')
        self.rootdir = os.path.join(self.workdir, 'root')
        self.configdir = os.path.join(self.workdir, 'config')
        os.mkdir(self.cachedir)
        os.mkdir(self.rootdir)
        os.mkdir(self.configdir)
        os.mkdir(os.path.join(self.configdir, 'Foonux 1.2'))
        with open(os.path.join(self.configdir, 'Foonux 1.2', 'sources.list'),
                  'w') as f:
            f.write('deb http://archive.ubuntu.com/ubuntu/ precise main\n')
            f.write('deb-src http://archive.ubuntu.com/ubuntu/ precise main\n')
            f.write('deb http://ddebs.ubuntu.com/ precise main\n')
            if updates:
                f.write(
                    'deb http://archive.ubuntu.com/ubuntu/ precise-updates main\n'
                )
                f.write(
                    'deb-src http://archive.ubuntu.com/ubuntu/ precise-updates main\n'
                )
                f.write('deb http://ddebs.ubuntu.com/ precise-updates main\n')
        os.mkdir(os.path.join(self.configdir, 'Foonux 1.2', 'armhf'))
        with open(
                os.path.join(self.configdir, 'Foonux 1.2', 'armhf',
                             'sources.list'), 'w') as f:
            f.write('deb http://ports.ubuntu.com/ precise main\n')
            f.write('deb-src http://ports.ubuntu.com/ precise main\n')
            f.write('deb http://ddebs.ubuntu.com/ precise main\n')
            if updates:
                f.write('deb http://ports.ubuntu.com/ precise-updates main\n')
                f.write(
                    'deb-src http://ports.ubuntu.com/ precise-updates main\n')
                f.write('deb http://ddebs.ubuntu.com/ precise-updates main\n')
        with open(os.path.join(self.configdir, 'Foonux 1.2', 'codename'),
                  'w') as f:
            f.write('precise')

    def assert_elf_arch(self, path, expected):
        '''Assert that an ELF file is for an expected machine type.

        Expected is a Debian-style architecture (i386, amd64, armhf)
        '''
        archmap = {
            'i386': '80386',
            'amd64': 'X86-64',
            'armhf': 'ARM',
        }

        # get ELF machine type
        readelf = subprocess.Popen(['readelf', '-e', path],
                                   env={},
                                   stdout=subprocess.PIPE,
                                   universal_newlines=True)
        out = readelf.communicate()[0]
        assert readelf.returncode == 0
        for line in out.splitlines():
            if line.startswith('  Machine:'):
                machine = line.split(maxsplit=1)[1]
                break
        else:
            self.fail('could not fine Machine: in readelf output')

        self.assertTrue(
            archmap[expected] in machine,
            '%s has unexpected machine type "%s" for architecture %s' %
            (path, machine, expected))
    def test_get_file_package_uninstalled(self):
        '''get_file_package() on uninstalled packages.'''

        # generate a test Contents.gz
        basedir = tempfile.mkdtemp()
        try:
            # test Contents.gz for release pocket
            mapdir = os.path.join(basedir, 'dists', impl.get_distro_codename())
            os.makedirs(mapdir)
            with gzip.open(
                    os.path.join(
                        mapdir,
                        'Contents-%s.gz' % impl.get_system_architecture()),
                    'w') as f:
                f.write(b'''
foo header
FILE                                                    LOCATION
usr/bin/frobnicate                                      foo/frob
usr/bin/frob                                            foo/frob-utils
bo/gu/s                                                 na/mypackage
bin/true                                                admin/superutils
''')

            # test Contents.gz for -updates pocket
            mapdir = os.path.join(basedir, 'dists',
                                  impl.get_distro_codename() + '-updates')
            os.makedirs(mapdir)
            with gzip.open(
                    os.path.join(
                        mapdir,
                        'Contents-%s.gz' % impl.get_system_architecture()),
                    'w') as f:
                f.write(b'''
foo header
FILE                                                    LOCATION
lib/libnew.so.5                                         universe/libs/libnew5
''')

            # use this as a mirror
            impl.set_mirror('file://' + basedir)

            self.assertEqual(impl.get_file_package('usr/bin/frob', False),
                             None)
            # must not match frob (same file name prefix)
            self.assertEqual(impl.get_file_package('usr/bin/frob', True),
                             'frob-utils')
            self.assertEqual(impl.get_file_package('/usr/bin/frob', True),
                             'frob-utils')
            # find files from -updates pocket
            self.assertEqual(impl.get_file_package('/lib/libnew.so.5', False),
                             None)
            self.assertEqual(impl.get_file_package('/lib/libnew.so.5', True),
                             'libnew5')

            # invalid mirror
            impl.set_mirror('file:///foo/nonexisting')
            self.assertRaises(IOError, impl.get_file_package, 'usr/bin/frob',
                              True)

            # valid mirror, test cache directory
            impl.set_mirror('file://' + basedir)
            cache_dir = os.path.join(basedir, 'cache')
            os.mkdir(cache_dir)
            self.assertEqual(
                impl.get_file_package('usr/bin/frob', True, cache_dir),
                'frob-utils')
            cache_dir_files = os.listdir(cache_dir)
            self.assertEqual(len(cache_dir_files), 2)
            self.assertEqual(
                impl.get_file_package('/bo/gu/s', True, cache_dir),
                'mypackage')

            # valid cache, should not need to access the mirror
            impl.set_mirror('file:///foo/nonexisting')
            self.assertEqual(
                impl.get_file_package('/bin/true', True, cache_dir),
                'superutils')
            self.assertEqual(
                impl.get_file_package('/bo/gu/s', True, cache_dir),
                'mypackage')
            self.assertEqual(
                impl.get_file_package('/lib/libnew.so.5', True, cache_dir),
                'libnew5')

            # outdated cache, must refresh the cache and hit the invalid
            # mirror
            if 'updates' in cache_dir_files[0]:
                cache_file = cache_dir_files[1]
            else:
                cache_file = cache_dir_files[0]
            now = int(time.time())
            os.utime(os.path.join(cache_dir, cache_file), (now, now - 90000))

            self.assertRaises(IOError, impl.get_file_package, '/bo/gu/s', True,
                              cache_dir)
        finally:
            shutil.rmtree(basedir)
Example #9
0
    def test_install_packages_versioned(self):
        '''install_packages() with versions and with cache'''

        self._setup_foonux_config()
        obsolete = impl.install_packages(self.rootdir, self.configdir,
                                         'Foonux 1.2',
                                         [('coreutils', '8.13-3ubuntu3'),
                                          ('libc6', '2.15-0ubuntu10'),
                                          ('tzdata', '2012b-1'),
                                         ], False, self.cachedir)

        self.assertEqual(obsolete, '')
        self.assertTrue(os.path.exists(os.path.join(self.rootdir,
                                                    'usr/bin/stat')))
        self.assert_elf_arch(os.path.join(self.rootdir, 'usr/bin/stat'),
                             impl.get_system_architecture())
        self.assertTrue(os.path.exists(os.path.join(self.rootdir,
                                                    'usr/lib/debug/usr/bin/stat')))
        self.assertTrue(os.path.exists(os.path.join(self.rootdir,
                                                    'usr/share/zoneinfo/zone.tab')))
        self.assertTrue(os.path.exists(os.path.join(self.rootdir,
                                                    'usr/share/doc/libc6/copyright')))

        # does not clobber config dir
        self.assertEqual(os.listdir(self.configdir), ['Foonux 1.2'])
        self.assertEqual(sorted(os.listdir(os.path.join(self.configdir, 'Foonux 1.2'))),
                         ['armhf', 'codename', 'sources.list'])
        self.assertEqual(os.listdir(os.path.join(self.configdir, 'Foonux 1.2', 'armhf')),
                         ['sources.list'])

        # caches packages
        cache = os.listdir(os.path.join(self.cachedir, 'Foonux 1.2', 'apt',
                                        'var', 'cache', 'apt', 'archives'))
        cache_names = [p.split('_')[0] for p in cache]
        self.assertTrue('coreutils' in cache_names)
        self.assertTrue('coreutils-dbgsym' in cache_names)
        self.assertTrue('tzdata' in cache_names)
        self.assertTrue('libc6' in cache_names)
        self.assertTrue('libc6-dbg' in cache_names)

        # installs cached packages
        os.unlink(os.path.join(self.rootdir, 'usr/bin/stat'))
        obsolete = impl.install_packages(self.rootdir, self.configdir,
                                         'Foonux 1.2',
                                         [('coreutils', '8.13-3ubuntu3'),
                                         ], False, self.cachedir)
        self.assertEqual(obsolete, '')
        self.assertTrue(os.path.exists(
            os.path.join(self.rootdir, 'usr/bin/stat')))

        # complains about obsolete packages
        result = impl.install_packages(self.rootdir, self.configdir,
                                       'Foonux 1.2', [('gnome-common', '1.1')])
        self.assertEqual(len(result.splitlines()), 1)
        self.assertTrue('gnome-common' in result)
        self.assertTrue('1.1' in result)
        # ... but installs the current version anyway
        self.assertTrue(os.path.exists(
            os.path.join(self.rootdir, 'usr/bin/gnome-autogen.sh')))

        # does not crash on nonexisting packages
        result = impl.install_packages(self.rootdir, self.configdir,
                                       'Foonux 1.2', [('buggerbogger', None)])
        self.assertEqual(len(result.splitlines()), 1)
        self.assertTrue('buggerbogger' in result)
        self.assertTrue('not exist' in result)

        # can interleave with other operations
        dpkg = subprocess.Popen(['dpkg-query', '-Wf${Version}', 'dash'],
                                stdout=subprocess.PIPE)
        coreutils_version = dpkg.communicate()[0].decode()
        self.assertEqual(dpkg.returncode, 0)

        self.assertEqual(impl.get_version('dash'), coreutils_version)
        self.assertRaises(ValueError, impl.get_available_version, 'buggerbogger')

        # still installs packages after above operations
        os.unlink(os.path.join(self.rootdir, 'usr/bin/stat'))
        impl.install_packages(self.rootdir, self.configdir, 'Foonux 1.2',
                              [('coreutils', '8.13-3ubuntu3'),
                               ('dpkg', None),
                              ], False, self.cachedir)
        self.assertTrue(os.path.exists(os.path.join(self.rootdir,
                                                    'usr/bin/stat')))
        self.assertTrue(os.path.exists(os.path.join(self.rootdir,
                                                    'usr/bin/dpkg')))
    def test_get_file_package_uninstalled(self):
        '''get_file_package() on uninstalled packages.'''

        # generate a test Contents.gz
        basedir = tempfile.mkdtemp()
        try:
            # test Contents.gz for release pocket
            mapdir = os.path.join(basedir, 'dists', impl.get_distro_codename())
            os.makedirs(mapdir)
            with gzip.open(os.path.join(mapdir, 'Contents-%s.gz' %
                                        impl.get_system_architecture()), 'w') as f:
                f.write(b'''
foo header
FILE                                                    LOCATION
usr/bin/frobnicate                                      foo/frob
usr/bin/frob                                            foo/frob-utils
bo/gu/s                                                 na/mypackage
''')

            # test Contents.gz for -updates pocket
            mapdir = os.path.join(basedir, 'dists', impl.get_distro_codename() + '-updates')
            os.makedirs(mapdir)
            with gzip.open(os.path.join(mapdir, 'Contents-%s.gz' %
                                        impl.get_system_architecture()), 'w') as f:
                f.write(b'''
foo header
FILE                                                    LOCATION
lib/libnew.so.5                                         universe/libs/libnew5
''')

            # use this as a mirror
            impl.set_mirror('file://' + basedir)

            self.assertEqual(impl.get_file_package('usr/bin/frob', False), None)
            # must not match frob (same file name prefix)
            self.assertEqual(impl.get_file_package('usr/bin/frob', True), 'frob-utils')
            self.assertEqual(impl.get_file_package('/usr/bin/frob', True), 'frob-utils')
            # find files from -updates pocket
            self.assertEqual(impl.get_file_package('/lib/libnew.so.5', False), None)
            self.assertEqual(impl.get_file_package('/lib/libnew.so.5', True), 'libnew5')

            # invalid mirror
            impl.set_mirror('file:///foo/nonexisting')
            self.assertRaises(IOError, impl.get_file_package, 'usr/bin/frob', True)

            # valid mirror, test cache directory
            impl.set_mirror('file://' + basedir)
            cache_dir = os.path.join(basedir, 'cache')
            os.mkdir(cache_dir)
            self.assertEqual(impl.get_file_package('usr/bin/frob', True, cache_dir), 'frob-utils')
            cache_dir_files = os.listdir(cache_dir)
            self.assertEqual(len(cache_dir_files), 2)
            self.assertEqual(impl.get_file_package('/bo/gu/s', True, cache_dir), 'mypackage')

            # valid cache, should not need to access the mirror
            impl.set_mirror('file:///foo/nonexisting')
            self.assertEqual(impl.get_file_package('/bo/gu/s', True, cache_dir), 'mypackage')
            self.assertEqual(impl.get_file_package('/lib/libnew.so.5', True, cache_dir), 'libnew5')

            # outdated cache, must refresh the cache and hit the invalid
            # mirror
            if 'updates' in cache_dir_files[0]:
                cache_file = cache_dir_files[1]
            else:
                cache_file = cache_dir_files[0]
            now = int(time.time())
            os.utime(os.path.join(cache_dir, cache_file), (now, now - 90000))

            self.assertRaises(IOError, impl.get_file_package, '/bo/gu/s', True, cache_dir)
        finally:
            shutil.rmtree(basedir)