#!/usr/bin/python
# pkgbinarymangler automatic tests
# (C) 2010 Canonical Ltd.
# Author: Martin Pitt <martin.pitt@ubuntu.com>
# License: GPL 2 or later

import pickle
import unittest
import subprocess
import tempfile
import shutil
import os.path
import sys
import tarfile
import re
from glob import glob

class T(unittest.TestCase):
    def setUp(self):
        self.srcdir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
        
        self.workdir = tempfile.mkdtemp()
        self.pkgdir = os.path.join(self.workdir, 'icecream')
        shutil.copytree(os.path.join(self.srcdir, 'test', 'icecream'),
                self.pkgdir)

        self.buildinfo = None

        os.environ['PKGBINARYMANGLER_COMMON_PATH'] = self.srcdir
        # locally fake the dpkg diversion
        if os.path.exists('/usr/bin/dpkg-deb.pkgbinarymangler'):
            os.symlink('/usr/bin/dpkg-deb.pkgbinarymangler', os.path.join(self.srcdir, 'dpkg-deb.pkgbinarymangler'))
        else:
            os.symlink('/usr/bin/dpkg-deb', os.path.join(self.srcdir, 'dpkg-deb.pkgbinarymangler'))

        # copy our default configuration files, and enable them
        for conf in glob(os.path.join(self.srcdir, '*.conf')):
            f = open(os.path.join(self.workdir, os.path.basename(conf)), 'w')
            f.write(open(conf).read().replace('enable: false', 'enable: true'))
            f.close()
        os.environ['PKGBINARYMANGLER_CONF_DIR'] = self.workdir

        os.environ['PKMAINTAINERGMANGLER_OVERRIDES'] = os.path.join(
                self.srcdir, 'maintainermangler.overrides')

        # point to local debhelper sequencer
        d = os.path.join(self.workdir, 'Debian', 'Debhelper', 'Sequence')
        os.makedirs(d)
        shutil.copy('translations.pm', d)
        os.environ['PERLLIB'] = '%s:%s' % (self.workdir, os.environ.get('PERLLIB', ''))

    def tearDown(self):
        shutil.rmtree(self.workdir)
        os.unlink(os.path.join(self.srcdir, 'dpkg-deb.pkgbinarymangler'))

    def build(self, use_local_mangler=True, extra_env=None, srcname='icecream'):
        env = os.environ.copy()
        if use_local_mangler:
            env['PATH'] = self.srcdir + ':' + env.get('PATH', '')

            bi = os.path.join(self.workdir, 'CurrentlyBuilding')
            env['CURRENTLY_BUILDING_PATH'] = bi
            # ignore system apt sources, otherwise we'll break some tests if we
            # run them in an OEM buildd environment
            env['PKGBINARYMANGLER_APT_CONF_DIR'] = self.workdir
            if self.buildinfo:
                f = open(bi, 'w')
                f.write(self.buildinfo)
                f.close()
        else:
            # disable a system-installed pkgbinarymangler
            if os.path.exists('/usr/bin/dpkg-deb.pkgbinarymangler'):
                os.symlink('/usr/bin/dpkg-deb.pkgbinarymangler',
                        os.path.join(self.workdir, 'dpkg-deb'))
            # make dh_translations available in $PATH
            os.symlink(os.path.join(self.srcdir, 'dh_translations'),
                       os.path.join(self.workdir, 'dh_translations'))
            env['PATH'] = self.workdir + ':' + env.get('PATH', '')

        if extra_env:
            env.update(extra_env)

        build = subprocess.Popen(['dpkg-buildpackage', '-us', '-uc', '-b'],
                stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                cwd=self.pkgdir, env=env)
        out = build.communicate()[0]
        self.assertEqual(build.returncode, 0, 
            '--- dpkg failed with error %i:\n%s\n-----' % (build.returncode, out))

        self.changes = open(glob(os.path.join(self.workdir, '%s_12*_*.changes' % srcname))[0]).read()

        self.translations_tar = glob(os.path.join(self.workdir, '%s_12*_*_translations.tar.gz' % srcname))
        if len(self.translations_tar) == 0:
            self.translations_tar = None
        else:
            assert len(self.translations_tar) == 1
            self.translations_tar = self.translations_tar[0]

    def check_deb_mo(self, expect_mo):
        '''Verify the built .deb contents for translations'''

        for pkg in ('vanilla', 'chocolate'):
            deb = glob(os.path.join(self.workdir, '%s_12_*.deb' % pkg))[0]
            dpkg = subprocess.Popen(['dpkg', '-c', deb],
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            (out, err) = dpkg.communicate()
            if expect_mo:
                self.assert_('./usr/share/locale/fr/LC_MESSAGES/%s.mo' % pkg in out)
                self.assert_('./usr/share/locale/de/LC_MESSAGES/%s.mo' % pkg in out)
            else:
                self.failIf('/usr/share/locale/' in out)
            self.assert_('./usr/share/doc/%s/copyright' % pkg in out)

    def check_deb_stripfiles(self, expect_files):
        '''Verify the built .deb contents for files we want to strip'''

        for pkg in ('vanilla', 'chocolate'):
            deb = glob(os.path.join(self.workdir, '%s_12*_*.deb' % pkg))[0]
            dpkg = subprocess.Popen(['dpkg', '-c', deb],
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            (out, err) = dpkg.communicate()
            if expect_files:
                self.assert_('./usr/share/doc/%s/buildinfo.gz' % pkg in out)
                self.assert_('./usr/share/doc/%s/changelog.gz' % pkg in out or 
                             './usr/share/doc/%s/changelog.Debian.gz' % pkg in out)
            else:
                self.failIf('./usr/share/doc/%s/buildinfo.gz' % pkg in out)
                if 'changelog.Debian.gz' in out:
                    self.failIf('changelog.gz' in out)

            dpkg = subprocess.Popen('ar p %s control.tar.gz | tar xOz ./md5sums' % deb, shell=True,
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            (out, err) = dpkg.communicate()
            self.assertEqual(err, '')
            if expect_files:
                self.assert_('usr/share/doc/%s/buildinfo.gz' % pkg in out)
                self.assert_('usr/share/doc/%s/changelog.gz' % pkg in out or 
                             'usr/share/doc/%s/changelog.Debian.gz' % pkg in out)
            else:
                self.failIf('buildinfo.gz' in out)
                if 'changelog.Debian.gz' in out:
                    self.failIf('changelog.gz' in out)

    def check_maintainer(self, expect_mangle):
        '''Verify the built .deb for mangled maintainer'''

        for deb in glob(os.path.join(self.workdir, '*_12_*.deb')):
            dpkg = subprocess.Popen(['dpkg', '-I', deb],
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            (out, err) = dpkg.communicate()
            self.assertEqual(err, '')
            if expect_mangle:
                self.assert_('Maintainer: Ubuntu' in out)
                self.assert_('Original-Maintainer: Joe User <joe@example.com>' in out)
            else:
                self.assert_('Maintainer: Joe User <joe@example.com>' in out)
                self.failIf('Original-Maintainer:' in out)

    def check_translation_tarball(self):
        '''Verify _translations.tar.gz integrity'''

        self.assert_('raw-translations - ' + os.path.basename(self.translations_tar) in self.changes)

        tar = tarfile.open(self.translations_tar)
        self.assert_('./vanilla/usr/share/locale/fr/LC_MESSAGES/vanilla.mo' 
                in tar.getnames())
        self.assert_('./chocolate/usr/share/locale/de/LC_MESSAGES/chocolate.mo' 
                in tar.getnames())
        self.assert_('./source/po-vanilla/vanilla.pot'
                in tar.getnames())
        self.assert_('./source/po-chocolate/de.po'
                in tar.getnames())

    def check_deb_integrity(self):
        '''Check that we can properly unpack the generated .debs
        
        This also verifies md5sums.
        '''
        debs = glob(os.path.join(self.workdir, '*.deb'))
        if not debs:
            self.fail('No .debs produced')

        for deb in debs:
            extractdir = os.path.join(self.workdir, 'deb-' +
                    os.path.splitext(os.path.basename(deb))[0])

            env = os.environ.copy()
            env['PATH'] = self.srcdir + ':' + env.get('PATH', '')
            dpkg = subprocess.Popen(['dpkg-deb', '-x', deb, extractdir],
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
            (out, err) = dpkg.communicate()

            # dpkg-deb must not print anything, otherwise apt falls over
            self.assertEqual(out, '')
            self.assertEqual(err, '')
            self.assertEqual(dpkg.returncode, 0)

            # verify md5sums
            md5sum = subprocess.Popen('ar p %s control.tar.gz | tar xOz ./md5sums | md5sum -c' % deb, 
                    shell=True, cwd=extractdir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            out, err = md5sum.communicate()
            self.assertEqual(err, '', out + err)
            self.assertEqual(md5sum.returncode, 0)

            # our link to .png file must remain a link
            foolink = os.path.join(extractdir, 'usr', 'share', 'icecream',
                    'foolink.png')
            if os.path.exists(foolink):
                self.assert_(os.path.islink(foolink))
                self.assertEqual(os.readlink(foolink), 'foo%.png')

    def test_no_mangler(self):
        '''No pkgbinarymangler'''

        self.build(False)
        self.check_deb_mo(True)
        self.check_maintainer(False)
        self.check_deb_stripfiles(True)

    def test_no_pkg_mangle(self):
        '''$NO_PKG_MANGLE disables pkgbinarymangler'''

        self.build(True, {'NO_PKG_MANGLE': '1'})
        self.check_deb_mo(True)
        self.check_maintainer(False)
        self.check_deb_stripfiles(True)
        self.assertEqual(self.translations_tar, None)
        self.check_deb_integrity()

    def test_no_buildinfo(self):
        '''No build info'''

        # this should always strip
        self.build()
        self.check_deb_mo(False)
        self.check_maintainer(True)
        self.check_deb_stripfiles(False)
        self.check_translation_tarball()
        self.check_deb_integrity()

    def test_main(self):
        '''Component: main'''

        self.buildinfo = '''Package: icecream
Component: main
Suite: lucid
Purpose: PRIMARY
'''

        # as configured by default, universe packages are not stripped, but
        # produce a tarball
        self.build()
        self.check_deb_mo(False)
        self.check_deb_stripfiles(False)
        self.check_maintainer(True)
        self.check_translation_tarball()
        self.check_deb_integrity()

    def test_universe(self):
        '''Component: universe'''

        self.buildinfo = '''Package: icecream
Component: universe
Suite: lucid
Purpose: PRIMARY
'''

        # as configured by default, universe packages are not stripped, but
        # produce a tarball
        self.build()
        self.check_deb_mo(True)
        self.check_maintainer(True)
        self.check_deb_stripfiles(False)
        self.check_translation_tarball()

    def test_ppa(self):
        '''Purpose: PPA'''

        self.buildinfo = '''Package: icecream
Component: main
Suite: lucid
Purpose: PPA
'''

        # PPA builds are never touched by default
        self.build()
        self.check_deb_mo(True)
        self.check_maintainer(False)
        self.check_deb_stripfiles(True)
        self.assertEqual(self.translations_tar, None)
        self.check_deb_integrity()

    def test_partner(self):
        '''Section: partner'''

        cpath = os.path.join(self.pkgdir, 'debian', 'control')
        contents = open(cpath).readlines()
        for l in range(len(contents)):
            if contents[l].startswith('Section:'):
                contents[l] = 'Section: partner\n'
                break
        f = open(cpath, 'w')
        f.write(''.join(contents))
        f.close()

        # partner builds are not touched
        self.build()
        self.check_deb_mo(True)
        self.check_maintainer(False)
        self.check_deb_stripfiles(True)
        self.assertEqual(self.translations_tar, None)

    def test_langpack(self):
        '''language packs are not stripped'''

        self.buildinfo = '''Package: language-pack-de
Component: main
Suite: lucid
Purpose: PRIMARY
'''

        # rename source to langpack
        assert subprocess.call(['sed', '-i', 's/icecream/language-pack-de/g', 
            os.path.join(self.pkgdir, 'debian', 'control'), 
            os.path.join(self.pkgdir, 'debian', 'changelog')]) == 0
        self.build(True, srcname='language-pack-de')

        # should not strip translations, since partner is blacklisted
        self.check_deb_mo(True)
        self.check_deb_stripfiles(False)
        self.assertEqual(self.translations_tar, None)

    def test_ppa_oem_non_main(self):
        '''OEM PPA for non-main package'''

        # these currently look like normal PPAs, we can't yet determine the PPA
        # name from buildinfo; hack around with checking apt sources
        self.buildinfo = '''Package: icecream
Component: main
Suite: lucid
Purpose: PPA
'''
        apt_sources = open(os.path.join(self.workdir, 'sources.list'), 'w')
        apt_sources.write('''deb http://private-ppa.buildd/oem-archive/myoem/ubuntu lucid main
deb https://cesg.canonical.com/canonical myoem public private
deb https://cesg.canonical.com/canonical myoem-devel public private
deb http://ftpmaster.internal/ubuntu lucid main restricted universe multiverse
''')
        apt_sources.close()

        # those should not strip translations, since it's not in main
        self.build(True, {'PKGBINARYMANGLER_APT_CONF_DIR': self.workdir})
        self.check_deb_mo(True)
        self.check_deb_stripfiles(False)
        self.check_translation_tarball()

        # no maintainer mangling in PPAs, as usual
        self.check_maintainer(False)

    def test_ppa_oem_main(self):
        '''OEM PPA for main package'''

        # these currently look like normal PPAs, we can't yet determine the PPA
        # name from buildinfo; hack around with checking apt sources
        self.buildinfo = '''Package: icecream
Component: main
Suite: lucid
Purpose: PPA
'''
        apt_sources = open(os.path.join(self.workdir, 'sources.list'), 'w')
        apt_sources.write('''deb http://private-ppa.buildd/oem-archive/myoem/ubuntu lucid main
deb https://cesg.canonical.com/canonical myoem public private
deb https://cesg.canonical.com/canonical myoem-devel public private
deb http://ftpmaster.internal/ubuntu lucid main restricted universe multiverse
''')
        apt_sources.close()

        # rename binaries to two in main
        assert subprocess.call(['sed', '-i', 's/vanilla/coreutils/g; s/chocolate/bash/g', 
            os.path.join(self.pkgdir, 'debian', 'control')]) == 0
        os.rename(os.path.join(self.pkgdir, 'debian', 'vanilla.install'),
                os.path.join(self.pkgdir, 'debian', 'coreutils.install'))
        os.rename(os.path.join(self.pkgdir, 'debian', 'chocolate.install'),
                os.path.join(self.pkgdir, 'debian', 'bash.install'))

        self.build(True, {'PKGBINARYMANGLER_APT_CONF_DIR': self.workdir})

        # should strip translations, since the are in Ubuntu main
        for pkg in ('coreutils', 'bash'):
            deb = glob(os.path.join(self.workdir, '%s_12_*.deb' % pkg))[0]
            dpkg = subprocess.Popen(['dpkg', '-c', deb],
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            (out, err) = dpkg.communicate()
            self.failIf('/usr/share/locale/' in out)
            self.assert_('./usr/share/doc/%s/copyright' % pkg in out)

        tar = tarfile.open(self.translations_tar)
        self.assert_('./coreutils/usr/share/locale/fr/LC_MESSAGES/vanilla.mo' 
                in tar.getnames())
        self.assert_('./bash/usr/share/locale/de/LC_MESSAGES/chocolate.mo' 
                in tar.getnames())
        self.assert_('./source/po-vanilla/vanilla.pot'
                in tar.getnames())
        self.assert_('./source/po-chocolate/de.po'
                in tar.getnames())

        # no maintainer mangling in PPAs, as usual
        self.check_maintainer(False)

    def test_ppa_oem_main_blacklisted(self):
        '''OEM PPA for main package for blacklisted project'''

        # these currently look like normal PPAs, we can't yet determine the PPA
        # name from buildinfo; hack around with checking apt sources
        self.buildinfo = '''Package: icecream
Component: main
Suite: lucid
Purpose: PPA
'''
        apt_sources = open(os.path.join(self.workdir, 'sources.list'), 'w')
        apt_sources.write('''deb http://private-ppa.buildd/oem-archive/partner/ubuntu lucid main
deb https://cesg.canonical.com/canonical partner public private
deb https://cesg.canonical.com/canonical partner-devel public private
deb http://ftpmaster.internal/ubuntu lucid main restricted universe multiverse
''')
        apt_sources.close()

        # rename binaries to two in main
        assert subprocess.call(['sed', '-i', 's/vanilla/coreutils/g; s/chocolate/bash/g', 
            os.path.join(self.pkgdir, 'debian', 'control')]) == 0
        os.rename(os.path.join(self.pkgdir, 'debian', 'vanilla.install'),
                os.path.join(self.pkgdir, 'debian', 'coreutils.install'))
        os.rename(os.path.join(self.pkgdir, 'debian', 'chocolate.install'),
                os.path.join(self.pkgdir, 'debian', 'bash.install'))

        self.build(True, {'PKGBINARYMANGLER_APT_CONF_DIR': self.workdir})

        # should not strip translations, since partner is blacklisted
        for pkg in ('coreutils', 'bash'):
            deb = glob(os.path.join(self.workdir, '%s_12_*.deb' % pkg))[0]
            dpkg = subprocess.Popen(['dpkg', '-c', deb],
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            (out, err) = dpkg.communicate()
            if pkg == 'coreutils':
                self.assert_('./usr/share/locale/fr/LC_MESSAGES/vanilla.mo' in out)
            else:
                self.assert_('./usr/share/locale/de/LC_MESSAGES/chocolate.mo' in out)
            self.assert_('./usr/share/doc/%s/copyright' % pkg in out)

        self.assertEqual(self.translations_tar, None)

        # no maintainer mangling in PPAs, as usual
        self.check_maintainer(False)

    def test_installed_size(self):
        '''Installed-Size gets updated'''

        # add some bloat to the vanilla po to get a recognizable size increase
        f = open(os.path.join(self.pkgdir, 'po-vanilla', 'de.po'), 'a')
        for i in xrange(10000):
            f.write('\nmsgid "%i"\nmsgstr"%i"\n' % (i, i))
        f.close()

        self.build()

        dpkg = subprocess.Popen(['dpkg', '-I', os.path.join(self.workdir, 'vanilla_12_all.deb')],
                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        try:
            for l in dpkg.stdout:
                l = l.strip()
                if l.startswith('Installed-Size:'):
                    self.assert_(re.match('^Installed-Size: \d+$', l))
                    self.assert_(int(l.split()[1]) < 200)
                    break
            else:
                self.fail('No Installed-Size: header')
        finally:
            self.assertEqual(dpkg.wait(), 0)

        self.check_deb_integrity()

    def test_debian_changelog_truncation(self):
        '''Long Debian changelog gets truncated'''

        # change into non-native package
        cpath = os.path.join(self.pkgdir, 'debian', 'changelog')
        contents = open(cpath).readlines()
        contents[0] = contents[0].replace('(12)', '(12-1)')
        f = open(cpath, 'w')
        f.write(''.join(contents))
        f.close()

        self.build()
        self.check_deb_stripfiles(False)
        self.check_deb_integrity()

        deb = glob(os.path.join(self.workdir, 'vanilla_12*_*.deb'))[0]
        dpkg = subprocess.Popen('ar p %s data.tar.gz | tar xOz ./usr/share/doc/vanilla/changelog.Debian.gz | gzip -cd' % deb, 
                shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        (out, err) = dpkg.communicate()
        self.assertEqual(err, '')
        self.assert_('icecream (12-1)' in out)
        self.assert_('\n  * release 12' in out)
        self.assert_('icecream (3)' in out)
        self.failIf('icecream (2)' in out)
        self.failIf('icecream (1)' in out)
        self.assert_('apt-get changelog' in out)

    def test_native_changelog_truncation(self):
        '''Long Debian changelog for native packages gets truncated'''

        self.build()
        self.check_deb_stripfiles(False)
        self.check_deb_integrity()

        deb = glob(os.path.join(self.workdir, 'vanilla_12*_*.deb'))[0]
        dpkg = subprocess.Popen('ar p %s data.tar.gz | tar xOz ./usr/share/doc/vanilla/changelog.gz | gzip -cd' % deb, 
                shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        (out, err) = dpkg.communicate()
        self.assertEqual(err, '')
        self.assert_('icecream (12)' in out)
        self.assert_('\n  * release 12' in out)
        self.assert_('icecream (3)' in out)
        self.failIf('icecream (2)' in out)
        self.failIf('icecream (1)' in out)
        self.assert_('apt-get changelog' in out)

    def test_short_debian_changelog(self):
        '''Short Debian changelog remains unaltered'''

        # change into non-native package
        cpath = os.path.join(self.pkgdir, 'debian', 'changelog')
        contents = open(cpath).readlines()[:11]
        contents[0] = contents[0].replace('(12)', '(12-1)')
        f = open(cpath, 'w')
        f.write(''.join(contents))
        f.close()

        self.build()
        self.check_deb_stripfiles(False)
        self.check_deb_integrity()

        deb = glob(os.path.join(self.workdir, 'vanilla_12*_*.deb'))[0]
        dpkg = subprocess.Popen('ar p %s data.tar.gz | tar xOz ./usr/share/doc/vanilla/changelog.Debian.gz | gzip -cd' % deb, 
                shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        (out, err) = dpkg.communicate()
        self.assertEqual(err, '')
        self.assert_('icecream (12-1)' in out)
        self.assert_('icecream (11)' in out)
        self.failIf('icecream (10)' in out)
        self.failIf('apt-get changelog' in out)

    def test_short_native_changelog(self):
        '''Short Debian changelog for native packages remains unaltered'''

        # truncate changelog
        cpath = os.path.join(self.pkgdir, 'debian', 'changelog')
        contents = open(cpath).readlines()[:11]
        f = open(cpath, 'w')
        f.write(''.join(contents))
        f.close()

        self.build()
        self.check_deb_stripfiles(False)
        self.check_deb_integrity()

        deb = glob(os.path.join(self.workdir, 'vanilla_12*_*.deb'))[0]
        dpkg = subprocess.Popen('ar p %s data.tar.gz | tar xOz ./usr/share/doc/vanilla/changelog.gz | gzip -cd' % deb, 
                shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        (out, err) = dpkg.communicate()
        self.assertEqual(err, '')
        self.assert_('icecream (12)' in out)
        self.assert_('icecream (11)' in out)
        self.failIf('icecream (10)' in out)
        self.failIf('apt-get changelog' in out)

    def test_ppa_debian_changelog(self):
        '''Debian changelog in PPA build remains unaltered'''

        self.buildinfo = '''Package: icecream
Component: main
Suite: lucid
Purpose: PPA
'''

        # change into non-native package
        cpath = os.path.join(self.pkgdir, 'debian', 'changelog')
        contents = open(cpath).readlines()
        contents[0] = contents[0].replace('(12)', '(12-1)')
        f = open(cpath, 'w')
        f.write(''.join(contents))
        f.close()

        self.build()
        self.check_deb_stripfiles(True)
        self.check_deb_integrity()

        deb = glob(os.path.join(self.workdir, 'vanilla_12*_*.deb'))[0]
        dpkg = subprocess.Popen('ar p %s data.tar.gz | tar xOz ./usr/share/doc/vanilla/changelog.Debian.gz | gzip -cd' % deb, 
                shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        (out, err) = dpkg.communicate()
        self.assertEqual(err, '')
        self.assert_('icecream (12-1)' in out)
        self.assert_('icecream (11)' in out)
        self.assert_('icecream (1)' in out)
        self.failIf('apt-get changelog' in out)

    def test_png_shrinking(self):
        '''PNGs get optimized, and non-PNGs unmodified'''

        self.build()
        self.check_deb_integrity()

        deb = glob(os.path.join(self.workdir, 'vanilla_12_*.deb'))[0]
        self.assertEqual(subprocess.call(['dpkg', '-x', deb, self.workdir]), 0)

        png_orig = os.path.join(self.pkgdir, 'foo%.png')
        png_ship = os.path.join(self.workdir, 'usr', 'share', 'icecream', 'foo%.png')

        # shipped PNG should be visually identical to original
        subprocess.call(['convert', '-depth', '24', '-compress', 'none', png_orig,
            'ppm:%s/orig.ppm' % self.workdir])
        subprocess.call(['convert', '-depth', '24', '-compress', 'none', png_ship,
            'ppm:%s/ship.ppm' % self.workdir])
        # we need to filter out comments
        orig = ''
        for l in open(os.path.join(self.workdir, 'orig.ppm')):
            if not l.startswith('#'):
                orig += l
        ship = ''
        for l in open(os.path.join(self.workdir, 'ship.ppm')):
            if not l.startswith('#'):
                ship += l
        self.assertEqual(orig, ship)

        # shipped PNG should be smaller than original
        self.assert_(os.path.getsize(png_ship) < os.path.getsize(png_orig))

        # non-PNG should be unmodified
        nopng_orig = open(os.path.join(self.pkgdir, 'notapng.png')).read()
        nopng_ship = open(os.path.join(self.workdir, 'usr', 'share',
            'icecream', 'notapng.png')).read()
        self.assertEqual(nopng_orig, nopng_ship)

    def test_png_games(self):
        '''PNGs in Section: games are left unmodified'''

        # change section to games
        cpath = os.path.join(self.pkgdir, 'debian', 'control')
        contents = []
        for line in open(cpath):
            if line.startswith('Section:'):
                line = 'Section: games\n'
            contents.append(line)
        f = open(cpath, 'w')
        f.write(''.join(contents))
        f.close()

        self.build()
        self.check_deb_integrity()

        deb = glob(os.path.join(self.workdir, 'vanilla_12_*.deb'))[0]
        self.assertEqual(subprocess.call(['dpkg', '-x', deb, self.workdir]), 0)

        orig = open(os.path.join(self.pkgdir, 'foo%.png')).read()
        ship = open(os.path.join(self.workdir, 'usr', 'share',
            'icecream', 'foo%.png')).read()
        self.assert_(orig == ship, 'PNG is left unmodified')

        orig = open(os.path.join(self.pkgdir, 'notapng.png')).read()
        ship = open(os.path.join(self.workdir, 'usr', 'share',
            'icecream', 'notapng.png')).read()
        self.assert_(orig == ship, 'non-PNG is left unmodified')

    def test_png_disable_optimization(self):
        '''$NO_PNG_PKG_MANGLE disables PNG optimization'''

        self.build(True, {'NO_PNG_PKG_MANGLE': '1'})
        self.check_deb_integrity()

        deb = glob(os.path.join(self.workdir, 'vanilla_12_*.deb'))[0]
        self.assertEqual(subprocess.call(['dpkg', '-x', deb, self.workdir]), 0)

        orig = open(os.path.join(self.pkgdir, 'foo%.png')).read()
        ship = open(os.path.join(self.workdir, 'usr', 'share',
            'icecream', 'foo%.png')).read()
        self.assert_(orig == ship, 'PNG is left unmodified')

        orig = open(os.path.join(self.pkgdir, 'notapng.png')).read()
        ship = open(os.path.join(self.workdir, 'usr', 'share',
            'icecream', 'notapng.png')).read()
        self.assert_(orig == ship, 'non-PNG is left unmodified')

    def test_dpkg_deb_argv_handling(self):
        '''dpkg-deb doesn't split up arguments with whitespace in them'''

        # Wrap all the scripts that dpkg-deb wants to call with arguments
        for wrapped_cmd in ['pkgsanitychecks',
                            'pkgmaintainermangler',
                            'pkgstripfiles',
                            'pkgstriptranslations',
                            'dpkg-deb.pkgbinarymangler']:
            os.symlink(os.path.join(self.srcdir, 'test', 'pickle_argv'),
                       os.path.join(self.workdir, wrapped_cmd))

        # Run dpkg-deb
        env = os.environ.copy()
        env['PATH'] = '%s:%s' % (self.workdir, env['PATH'])
        dpkg_deb = os.path.join(self.srcdir, 'dpkg-deb')
        argv_test = subprocess.Popen(['pickle_argv', '-b', 'foo bar'],
                                     executable=dpkg_deb, env=env,
                                     stdout=subprocess.PIPE)
        out = argv_test.communicate()[0]

        # Extract the argv's with which they were all called.
        parts = out.split('\n--end--\n')
        argvs = []
        for part in parts[:-1]:
            argvs.append(pickle.loads(part))

        # Make the check
        for argv in argvs:
            self.assertEquals(argv[-1],
                              'foo bar',
                              'argument with space in it did not make it '
                              'through dpkg-deb alive (%r)' % (argv,))

    def test_dh_translations(self):
        '''dh_translations'''

        # this should always strip
        pot = os.path.join(self.pkgdir, 'po', 'icecream.pot')
        self.failIf(os.path.exists(pot))
        self.build()
        self.assert_(os.path.exists(pot))
        with open(pot) as f:
            lines = f.readlines()
            self.assert_('msgid "icecream"\n' in lines)

        # check *.desktop stripping
        expected_desktop = {'simple': '''[Desktop Entry]
Name=icecream
Comment=Yummy!
Exec=/usr/bin/vanilla
X-Ubuntu-Gettext-Domain=icecream
''',

                            'translated': '''[Desktop Entry]
Name=icecream
GenericName=desert
X-GNOME-FullName=Vanilla or Chocolate
Comment=Yummy!
Exec=/usr/bin/vanilla
X-Ubuntu-Gettext-Domain=icecream
''',

                            'leadingspace': '''
[Desktop Entry]
Name=icecream
Comment=Yummy!
Exec=/usr/bin/vanilla
X-Ubuntu-Gettext-Domain=icecream
''',

                            'multisection': '''[Desktop Entry]
Name=icecream
GenericName=desert
X-GNOME-FullName=Vanilla or Chocolate
Comment=Yummy!
Exec=/usr/bin/vanilla
X-Ubuntu-Gettext-Domain=icecream

X-Ayatana-Desktop-Shortcuts=Request

[Request Shortcut Group]
Name=Request some icecream
Exec=vanilla request:
OnlyShowIn=Messaging Menu
''',
                            'withdomain': '''[Desktop Entry]
Name=icecream
Comment=Yummy!
X-GNOME-Gettext-Domain=icecream
Exec=/usr/bin/vanilla
''',
                            'withdomain2': '''[Desktop Entry]
Name=icecream
Comment=Yummy!
Exec=/usr/bin/vanilla
X-Ubuntu-Gettext-Domain=icecream
''',
            }
                
        for name, expected in expected_desktop.items():
            with open(os.path.join(self.pkgdir, 
                'debian/vanilla/usr/share/applications/%s.desktop' % name)) as f:
                actual = f.read()
                self.assertEqual(actual, expected, '%s.desktop mismatch:\n"%s"' % (name, actual))

        # check *.server domain addition
        with open(os.path.join(self.pkgdir, 'debian/vanilla/usr/lib/bonobo/servers/bonobo.server')) as f:
            self.assertEqual(f.read(), '''<oaf_info>

<oaf_server ubuntu-gettext-domain="icecream" iid="OAFIID:IcecreamExample" type="exe" 
	location="/usr/lib/bonobo-2.0/samples/bonobo-icecream">
	<oaf_attribute name="name" type="string" value="Vanilla example"/>
	<oaf_attribute name="name-de" type="string" value="Vanille-Beispiel"/>
</oaf_server>

<oaf_server ubuntu-gettext-domain="icecream" iid="OAFIID:AnotherExample" type="factory" 
	location="OAFIID:Vanilla_Factory">
	<oaf_attribute name="name" type="string" value="Icecream component"/>
	<oaf_attribute name="name-de" type="string" value="Eiscreme-Komponente"/>
</oaf_server>

</oaf_info>
''')

        # check *.policy domain addition and stripping
        with open(os.path.join(self.pkgdir, 'debian/vanilla/usr/share/polkit-1/actions/icecream.policy')) as f:
            self.assertEqual(f.read(), '''<?xml version="1.0" encoding="UTF-8"?>
<policyconfig>

  <vendor>World Ice, Inc.</vendor>

  <action id="icecream.info">
    <description gettext-domain="icecream">Get information about ice cream</description>
    <message gettext-domain="icecream">System policy prevents querying icecream info</message>
    <defaults>
      <allow_any>yes</allow_any>
    </defaults>
  </action>
</policyconfig>
''')

        # check *.schemas domain addition and stripping
        with open(os.path.join(self.pkgdir, 'debian/vanilla/usr/share/gconf/schemas/icecream.schemas')) as f:
            actual = f.read()
            expected = '''<?xml version="1.0"?>
<gconfschemafile>
<schemalist>
    <schema>
	<key>/apps/icecream/simple</key>
	<owner>icecream</owner>
	<type>bool</type>
	<default>FALSE</default>
	<gettext_domain>icecream</gettext_domain>
	<locale name="C">
	    <short>Simple bool option</short>
	    <long>Bool doc</long>
	</locale>
    </schema>

    <schema>
	<key>/apps/icecream/localedefault</key>
	<owner>icecream</owner>
	<type>string</type>
	<default>xxx</default>
	<gettext_domain>icecream</gettext_domain>
	<locale name="C">
	    <default>CCC</default>
	    <short>Locale dependent default option</short>
	    <long>Locale doc</long>
	</locale>
	<locale name="de">
	    <default>dede</default>
	</locale>
    </schema>

 </schemalist>
</gconfschemafile>
'''
            self.assertEqual(actual, expected)

    def test_dh_translations_no_domain(self):
        '''dh_translations on package without po/Makefile'''

        os.remove(os.path.join(self.pkgdir, 'po', 'Makefile'))
        self.build()

        # should not touch files
        file_pairs = [
            (os.path.join(self.pkgdir, 'data/translated.desktop'),
             os.path.join(self.pkgdir, 'debian/vanilla/usr/share/applications/translated.desktop')),
            (os.path.join(self.pkgdir, 'data/icecream.schemas'),
             os.path.join(self.pkgdir, 'debian/vanilla/usr/share/gconf/schemas/icecream.schemas')),
            ]
        for (orig, ship) in file_pairs:
            with open(orig) as forig:
                with open(ship) as fship:
                    self.assertEqual(forig.read(), fship.read())

    def test_dh_translations_python(self):
        '''dh_translations: Get domain from Python setup.cfg'''

        os.remove(os.path.join(self.pkgdir, 'po', 'Makefile'))
        with open(os.path.join(self.pkgdir, 'setup.cfg'), 'w') as f:
            f.write('[build_i18n]\ndomain=icecream\n')

        self.build()

        with open(os.path.join(self.pkgdir,
            'debian/vanilla/usr/share/applications/translated.desktop')) as f:
            self.assertTrue('X-Ubuntu-Gettext-Domain=icecream' in f.read())

    def test_dh_translations_cmake(self):
        '''dh_translations: Get domain from cmake CMakeLists. CMakeLists.txt'''

        os.remove(os.path.join(self.pkgdir, 'po', 'Makefile'))
        with open(os.path.join(self.pkgdir, 'CMakeLists.txt'), 'w') as f:
            f.write('set (GETTEXT_PACKAGE "icecream")\n')
        with open(os.path.join(self.pkgdir, 'po', 'POTFILES.in'), 'w') as f:
            f.write('test.c\n')

        pot = os.path.join(self.pkgdir, 'po', 'icecream.pot')
        self.failIf(os.path.exists(pot))
        self.build()

        self.assert_(os.path.exists(pot))
        with open(pot) as f:
            lines = f.readlines()
            self.assert_('msgid "icecream"\n' in lines)

#
# main
#

unittest.main()
